8c2ee593f70802fe5fda9c157bcb7cdc0be2d893
[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             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2834             
2835             Roo.log(this.height);
2836             Roo.log(Roo.lib.Dom.getViewportHeight(true));
2837             
2838             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2839             this.setSize(w,h);
2840         }
2841         
2842     },
2843
2844     setSize : function(w,h)
2845     {
2846         if (!w && !h) {
2847             return;
2848         }
2849         this.resizeTo(w,h);
2850     },
2851
2852     show : function() {
2853
2854         if (!this.rendered) {
2855             this.render();
2856         }
2857
2858         //this.el.setStyle('display', 'block');
2859         this.el.removeClass('hideing');        
2860         this.el.addClass('show');
2861  
2862         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2863             var _this = this;
2864             (function(){
2865                 this.el.addClass('in');
2866             }).defer(50, this);
2867         }else{
2868             this.el.addClass('in');
2869         }
2870
2871         // not sure how we can show data in here..
2872         //if (this.tmpl) {
2873         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2874         //}
2875
2876         Roo.get(document.body).addClass("x-body-masked");
2877         
2878         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2879         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2880         this.maskEl.addClass('show');
2881         
2882         this.resize();
2883         
2884         this.fireEvent('show', this);
2885
2886         // set zindex here - otherwise it appears to be ignored...
2887         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2888
2889         (function () {
2890             this.items.forEach( function(e) {
2891                 e.layout ? e.layout() : false;
2892
2893             });
2894         }).defer(100,this);
2895
2896     },
2897     hide : function()
2898     {
2899         if(this.fireEvent("beforehide", this) !== false){
2900             this.maskEl.removeClass('show');
2901             Roo.get(document.body).removeClass("x-body-masked");
2902             this.el.removeClass('in');
2903             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2904
2905             if(this.animate){ // why
2906                 this.el.addClass('hideing');
2907                 (function(){
2908                     if (!this.el.hasClass('hideing')) {
2909                         return; // it's been shown again...
2910                     }
2911                     this.el.removeClass('show');
2912                     this.el.removeClass('hideing');
2913                 }).defer(150,this);
2914                 
2915             }else{
2916                  this.el.removeClass('show');
2917             }
2918             this.fireEvent('hide', this);
2919         }
2920     },
2921     isVisible : function()
2922     {
2923         
2924         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2925         
2926     },
2927
2928     addButton : function(str, cb)
2929     {
2930
2931
2932         var b = Roo.apply({}, { html : str } );
2933         b.xns = b.xns || Roo.bootstrap;
2934         b.xtype = b.xtype || 'Button';
2935         if (typeof(b.listeners) == 'undefined') {
2936             b.listeners = { click : cb.createDelegate(this)  };
2937         }
2938
2939         var btn = Roo.factory(b);
2940
2941         btn.render(this.el.select('.modal-footer div').first());
2942
2943         return btn;
2944
2945     },
2946
2947     setDefaultButton : function(btn)
2948     {
2949         //this.el.select('.modal-footer').()
2950     },
2951     diff : false,
2952
2953     resizeTo: function(w,h)
2954     {
2955         // skip.. ?? why??
2956
2957         this.dialogEl.setWidth(w);
2958         if (this.diff === false) {
2959             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2960         }
2961
2962         this.bodyEl.setHeight(h-this.diff);
2963
2964         this.fireEvent('resize', this);
2965
2966     },
2967     setContentSize  : function(w, h)
2968     {
2969
2970     },
2971     onButtonClick: function(btn,e)
2972     {
2973         //Roo.log([a,b,c]);
2974         this.fireEvent('btnclick', btn.name, e);
2975     },
2976      /**
2977      * Set the title of the Dialog
2978      * @param {String} str new Title
2979      */
2980     setTitle: function(str) {
2981         this.titleEl.dom.innerHTML = str;
2982     },
2983     /**
2984      * Set the body of the Dialog
2985      * @param {String} str new Title
2986      */
2987     setBody: function(str) {
2988         this.bodyEl.dom.innerHTML = str;
2989     },
2990     /**
2991      * Set the body of the Dialog using the template
2992      * @param {Obj} data - apply this data to the template and replace the body contents.
2993      */
2994     applyBody: function(obj)
2995     {
2996         if (!this.tmpl) {
2997             Roo.log("Error - using apply Body without a template");
2998             //code
2999         }
3000         this.tmpl.overwrite(this.bodyEl, obj);
3001     }
3002
3003 });
3004
3005
3006 Roo.apply(Roo.bootstrap.Modal,  {
3007     /**
3008          * Button config that displays a single OK button
3009          * @type Object
3010          */
3011         OK :  [{
3012             name : 'ok',
3013             weight : 'primary',
3014             html : 'OK'
3015         }],
3016         /**
3017          * Button config that displays Yes and No buttons
3018          * @type Object
3019          */
3020         YESNO : [
3021             {
3022                 name  : 'no',
3023                 html : 'No'
3024             },
3025             {
3026                 name  :'yes',
3027                 weight : 'primary',
3028                 html : 'Yes'
3029             }
3030         ],
3031
3032         /**
3033          * Button config that displays OK and Cancel buttons
3034          * @type Object
3035          */
3036         OKCANCEL : [
3037             {
3038                name : 'cancel',
3039                 html : 'Cancel'
3040             },
3041             {
3042                 name : 'ok',
3043                 weight : 'primary',
3044                 html : 'OK'
3045             }
3046         ],
3047         /**
3048          * Button config that displays Yes, No and Cancel buttons
3049          * @type Object
3050          */
3051         YESNOCANCEL : [
3052             {
3053                 name : 'yes',
3054                 weight : 'primary',
3055                 html : 'Yes'
3056             },
3057             {
3058                 name : 'no',
3059                 html : 'No'
3060             },
3061             {
3062                 name : 'cancel',
3063                 html : 'Cancel'
3064             }
3065         ],
3066         
3067         zIndex : 10001
3068 });
3069 /*
3070  * - LGPL
3071  *
3072  * messagebox - can be used as a replace
3073  * 
3074  */
3075 /**
3076  * @class Roo.MessageBox
3077  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3078  * Example usage:
3079  *<pre><code>
3080 // Basic alert:
3081 Roo.Msg.alert('Status', 'Changes saved successfully.');
3082
3083 // Prompt for user data:
3084 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3085     if (btn == 'ok'){
3086         // process text value...
3087     }
3088 });
3089
3090 // Show a dialog using config options:
3091 Roo.Msg.show({
3092    title:'Save Changes?',
3093    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3094    buttons: Roo.Msg.YESNOCANCEL,
3095    fn: processResult,
3096    animEl: 'elId'
3097 });
3098 </code></pre>
3099  * @singleton
3100  */
3101 Roo.bootstrap.MessageBox = function(){
3102     var dlg, opt, mask, waitTimer;
3103     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3104     var buttons, activeTextEl, bwidth;
3105
3106     
3107     // private
3108     var handleButton = function(button){
3109         dlg.hide();
3110         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3111     };
3112
3113     // private
3114     var handleHide = function(){
3115         if(opt && opt.cls){
3116             dlg.el.removeClass(opt.cls);
3117         }
3118         //if(waitTimer){
3119         //    Roo.TaskMgr.stop(waitTimer);
3120         //    waitTimer = null;
3121         //}
3122     };
3123
3124     // private
3125     var updateButtons = function(b){
3126         var width = 0;
3127         if(!b){
3128             buttons["ok"].hide();
3129             buttons["cancel"].hide();
3130             buttons["yes"].hide();
3131             buttons["no"].hide();
3132             //dlg.footer.dom.style.display = 'none';
3133             return width;
3134         }
3135         dlg.footerEl.dom.style.display = '';
3136         for(var k in buttons){
3137             if(typeof buttons[k] != "function"){
3138                 if(b[k]){
3139                     buttons[k].show();
3140                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3141                     width += buttons[k].el.getWidth()+15;
3142                 }else{
3143                     buttons[k].hide();
3144                 }
3145             }
3146         }
3147         return width;
3148     };
3149
3150     // private
3151     var handleEsc = function(d, k, e){
3152         if(opt && opt.closable !== false){
3153             dlg.hide();
3154         }
3155         if(e){
3156             e.stopEvent();
3157         }
3158     };
3159
3160     return {
3161         /**
3162          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3163          * @return {Roo.BasicDialog} The BasicDialog element
3164          */
3165         getDialog : function(){
3166            if(!dlg){
3167                 dlg = new Roo.bootstrap.Modal( {
3168                     //draggable: true,
3169                     //resizable:false,
3170                     //constraintoviewport:false,
3171                     //fixedcenter:true,
3172                     //collapsible : false,
3173                     //shim:true,
3174                     //modal: true,
3175                 //    width: 'auto',
3176                   //  height:100,
3177                     //buttonAlign:"center",
3178                     closeClick : function(){
3179                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3180                             handleButton("no");
3181                         }else{
3182                             handleButton("cancel");
3183                         }
3184                     }
3185                 });
3186                 dlg.render();
3187                 dlg.on("hide", handleHide);
3188                 mask = dlg.mask;
3189                 //dlg.addKeyListener(27, handleEsc);
3190                 buttons = {};
3191                 this.buttons = buttons;
3192                 var bt = this.buttonText;
3193                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3194                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3195                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3196                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3197                 //Roo.log(buttons);
3198                 bodyEl = dlg.bodyEl.createChild({
3199
3200                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3201                         '<textarea class="roo-mb-textarea"></textarea>' +
3202                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3203                 });
3204                 msgEl = bodyEl.dom.firstChild;
3205                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3206                 textboxEl.enableDisplayMode();
3207                 textboxEl.addKeyListener([10,13], function(){
3208                     if(dlg.isVisible() && opt && opt.buttons){
3209                         if(opt.buttons.ok){
3210                             handleButton("ok");
3211                         }else if(opt.buttons.yes){
3212                             handleButton("yes");
3213                         }
3214                     }
3215                 });
3216                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3217                 textareaEl.enableDisplayMode();
3218                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3219                 progressEl.enableDisplayMode();
3220                 
3221                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3222                 var pf = progressEl.dom.firstChild;
3223                 if (pf) {
3224                     pp = Roo.get(pf.firstChild);
3225                     pp.setHeight(pf.offsetHeight);
3226                 }
3227                 
3228             }
3229             return dlg;
3230         },
3231
3232         /**
3233          * Updates the message box body text
3234          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3235          * the XHTML-compliant non-breaking space character '&amp;#160;')
3236          * @return {Roo.MessageBox} This message box
3237          */
3238         updateText : function(text)
3239         {
3240             if(!dlg.isVisible() && !opt.width){
3241                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3242                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3243             }
3244             msgEl.innerHTML = text || '&#160;';
3245       
3246             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3247             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3248             var w = Math.max(
3249                     Math.min(opt.width || cw , this.maxWidth), 
3250                     Math.max(opt.minWidth || this.minWidth, bwidth)
3251             );
3252             if(opt.prompt){
3253                 activeTextEl.setWidth(w);
3254             }
3255             if(dlg.isVisible()){
3256                 dlg.fixedcenter = false;
3257             }
3258             // to big, make it scroll. = But as usual stupid IE does not support
3259             // !important..
3260             
3261             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3262                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3263                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3264             } else {
3265                 bodyEl.dom.style.height = '';
3266                 bodyEl.dom.style.overflowY = '';
3267             }
3268             if (cw > w) {
3269                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3270             } else {
3271                 bodyEl.dom.style.overflowX = '';
3272             }
3273             
3274             dlg.setContentSize(w, bodyEl.getHeight());
3275             if(dlg.isVisible()){
3276                 dlg.fixedcenter = true;
3277             }
3278             return this;
3279         },
3280
3281         /**
3282          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3283          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3284          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3285          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3286          * @return {Roo.MessageBox} This message box
3287          */
3288         updateProgress : function(value, text){
3289             if(text){
3290                 this.updateText(text);
3291             }
3292             
3293             if (pp) { // weird bug on my firefox - for some reason this is not defined
3294                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3295                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3296             }
3297             return this;
3298         },        
3299
3300         /**
3301          * Returns true if the message box is currently displayed
3302          * @return {Boolean} True if the message box is visible, else false
3303          */
3304         isVisible : function(){
3305             return dlg && dlg.isVisible();  
3306         },
3307
3308         /**
3309          * Hides the message box if it is displayed
3310          */
3311         hide : function(){
3312             if(this.isVisible()){
3313                 dlg.hide();
3314             }  
3315         },
3316
3317         /**
3318          * Displays a new message box, or reinitializes an existing message box, based on the config options
3319          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3320          * The following config object properties are supported:
3321          * <pre>
3322 Property    Type             Description
3323 ----------  ---------------  ------------------------------------------------------------------------------------
3324 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3325                                    closes (defaults to undefined)
3326 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3327                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3328 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3329                                    progress and wait dialogs will ignore this property and always hide the
3330                                    close button as they can only be closed programmatically.
3331 cls               String           A custom CSS class to apply to the message box element
3332 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3333                                    displayed (defaults to 75)
3334 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3335                                    function will be btn (the name of the button that was clicked, if applicable,
3336                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3337                                    Progress and wait dialogs will ignore this option since they do not respond to
3338                                    user actions and can only be closed programmatically, so any required function
3339                                    should be called by the same code after it closes the dialog.
3340 icon              String           A CSS class that provides a background image to be used as an icon for
3341                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3342 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3343 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3344 modal             Boolean          False to allow user interaction with the page while the message box is
3345                                    displayed (defaults to true)
3346 msg               String           A string that will replace the existing message box body text (defaults
3347                                    to the XHTML-compliant non-breaking space character '&#160;')
3348 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3349 progress          Boolean          True to display a progress bar (defaults to false)
3350 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3351 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3352 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3353 title             String           The title text
3354 value             String           The string value to set into the active textbox element if displayed
3355 wait              Boolean          True to display a progress bar (defaults to false)
3356 width             Number           The width of the dialog in pixels
3357 </pre>
3358          *
3359          * Example usage:
3360          * <pre><code>
3361 Roo.Msg.show({
3362    title: 'Address',
3363    msg: 'Please enter your address:',
3364    width: 300,
3365    buttons: Roo.MessageBox.OKCANCEL,
3366    multiline: true,
3367    fn: saveAddress,
3368    animEl: 'addAddressBtn'
3369 });
3370 </code></pre>
3371          * @param {Object} config Configuration options
3372          * @return {Roo.MessageBox} This message box
3373          */
3374         show : function(options)
3375         {
3376             
3377             // this causes nightmares if you show one dialog after another
3378             // especially on callbacks..
3379              
3380             if(this.isVisible()){
3381                 
3382                 this.hide();
3383                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3384                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3385                 Roo.log("New Dialog Message:" +  options.msg )
3386                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3387                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3388                 
3389             }
3390             var d = this.getDialog();
3391             opt = options;
3392             d.setTitle(opt.title || "&#160;");
3393             d.closeEl.setDisplayed(opt.closable !== false);
3394             activeTextEl = textboxEl;
3395             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3396             if(opt.prompt){
3397                 if(opt.multiline){
3398                     textboxEl.hide();
3399                     textareaEl.show();
3400                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3401                         opt.multiline : this.defaultTextHeight);
3402                     activeTextEl = textareaEl;
3403                 }else{
3404                     textboxEl.show();
3405                     textareaEl.hide();
3406                 }
3407             }else{
3408                 textboxEl.hide();
3409                 textareaEl.hide();
3410             }
3411             progressEl.setDisplayed(opt.progress === true);
3412             this.updateProgress(0);
3413             activeTextEl.dom.value = opt.value || "";
3414             if(opt.prompt){
3415                 dlg.setDefaultButton(activeTextEl);
3416             }else{
3417                 var bs = opt.buttons;
3418                 var db = null;
3419                 if(bs && bs.ok){
3420                     db = buttons["ok"];
3421                 }else if(bs && bs.yes){
3422                     db = buttons["yes"];
3423                 }
3424                 dlg.setDefaultButton(db);
3425             }
3426             bwidth = updateButtons(opt.buttons);
3427             this.updateText(opt.msg);
3428             if(opt.cls){
3429                 d.el.addClass(opt.cls);
3430             }
3431             d.proxyDrag = opt.proxyDrag === true;
3432             d.modal = opt.modal !== false;
3433             d.mask = opt.modal !== false ? mask : false;
3434             if(!d.isVisible()){
3435                 // force it to the end of the z-index stack so it gets a cursor in FF
3436                 document.body.appendChild(dlg.el.dom);
3437                 d.animateTarget = null;
3438                 d.show(options.animEl);
3439             }
3440             return this;
3441         },
3442
3443         /**
3444          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3445          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3446          * and closing the message box when the process is complete.
3447          * @param {String} title The title bar text
3448          * @param {String} msg The message box body text
3449          * @return {Roo.MessageBox} This message box
3450          */
3451         progress : function(title, msg){
3452             this.show({
3453                 title : title,
3454                 msg : msg,
3455                 buttons: false,
3456                 progress:true,
3457                 closable:false,
3458                 minWidth: this.minProgressWidth,
3459                 modal : true
3460             });
3461             return this;
3462         },
3463
3464         /**
3465          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3466          * If a callback function is passed it will be called after the user clicks the button, and the
3467          * id of the button that was clicked will be passed as the only parameter to the callback
3468          * (could also be the top-right close button).
3469          * @param {String} title The title bar text
3470          * @param {String} msg The message box body text
3471          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3472          * @param {Object} scope (optional) The scope of the callback function
3473          * @return {Roo.MessageBox} This message box
3474          */
3475         alert : function(title, msg, fn, scope)
3476         {
3477             this.show({
3478                 title : title,
3479                 msg : msg,
3480                 buttons: this.OK,
3481                 fn: fn,
3482                 closable : false,
3483                 scope : scope,
3484                 modal : true
3485             });
3486             return this;
3487         },
3488
3489         /**
3490          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3491          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3492          * You are responsible for closing the message box when the process is complete.
3493          * @param {String} msg The message box body text
3494          * @param {String} title (optional) The title bar text
3495          * @return {Roo.MessageBox} This message box
3496          */
3497         wait : function(msg, title){
3498             this.show({
3499                 title : title,
3500                 msg : msg,
3501                 buttons: false,
3502                 closable:false,
3503                 progress:true,
3504                 modal:true,
3505                 width:300,
3506                 wait:true
3507             });
3508             waitTimer = Roo.TaskMgr.start({
3509                 run: function(i){
3510                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3511                 },
3512                 interval: 1000
3513             });
3514             return this;
3515         },
3516
3517         /**
3518          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3519          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3520          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3521          * @param {String} title The title bar text
3522          * @param {String} msg The message box body text
3523          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3524          * @param {Object} scope (optional) The scope of the callback function
3525          * @return {Roo.MessageBox} This message box
3526          */
3527         confirm : function(title, msg, fn, scope){
3528             this.show({
3529                 title : title,
3530                 msg : msg,
3531                 buttons: this.YESNO,
3532                 fn: fn,
3533                 scope : scope,
3534                 modal : true
3535             });
3536             return this;
3537         },
3538
3539         /**
3540          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3541          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3542          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3543          * (could also be the top-right close button) and the text that was entered will be passed as the two
3544          * parameters to the callback.
3545          * @param {String} title The title bar text
3546          * @param {String} msg The message box body text
3547          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3548          * @param {Object} scope (optional) The scope of the callback function
3549          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3550          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3551          * @return {Roo.MessageBox} This message box
3552          */
3553         prompt : function(title, msg, fn, scope, multiline){
3554             this.show({
3555                 title : title,
3556                 msg : msg,
3557                 buttons: this.OKCANCEL,
3558                 fn: fn,
3559                 minWidth:250,
3560                 scope : scope,
3561                 prompt:true,
3562                 multiline: multiline,
3563                 modal : true
3564             });
3565             return this;
3566         },
3567
3568         /**
3569          * Button config that displays a single OK button
3570          * @type Object
3571          */
3572         OK : {ok:true},
3573         /**
3574          * Button config that displays Yes and No buttons
3575          * @type Object
3576          */
3577         YESNO : {yes:true, no:true},
3578         /**
3579          * Button config that displays OK and Cancel buttons
3580          * @type Object
3581          */
3582         OKCANCEL : {ok:true, cancel:true},
3583         /**
3584          * Button config that displays Yes, No and Cancel buttons
3585          * @type Object
3586          */
3587         YESNOCANCEL : {yes:true, no:true, cancel:true},
3588
3589         /**
3590          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3591          * @type Number
3592          */
3593         defaultTextHeight : 75,
3594         /**
3595          * The maximum width in pixels of the message box (defaults to 600)
3596          * @type Number
3597          */
3598         maxWidth : 600,
3599         /**
3600          * The minimum width in pixels of the message box (defaults to 100)
3601          * @type Number
3602          */
3603         minWidth : 100,
3604         /**
3605          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3606          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3607          * @type Number
3608          */
3609         minProgressWidth : 250,
3610         /**
3611          * An object containing the default button text strings that can be overriden for localized language support.
3612          * Supported properties are: ok, cancel, yes and no.
3613          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3614          * @type Object
3615          */
3616         buttonText : {
3617             ok : "OK",
3618             cancel : "Cancel",
3619             yes : "Yes",
3620             no : "No"
3621         }
3622     };
3623 }();
3624
3625 /**
3626  * Shorthand for {@link Roo.MessageBox}
3627  */
3628 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3629 Roo.Msg = Roo.Msg || Roo.MessageBox;
3630 /*
3631  * - LGPL
3632  *
3633  * navbar
3634  * 
3635  */
3636
3637 /**
3638  * @class Roo.bootstrap.Navbar
3639  * @extends Roo.bootstrap.Component
3640  * Bootstrap Navbar class
3641
3642  * @constructor
3643  * Create a new Navbar
3644  * @param {Object} config The config object
3645  */
3646
3647
3648 Roo.bootstrap.Navbar = function(config){
3649     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3650     this.addEvents({
3651         // raw events
3652         /**
3653          * @event beforetoggle
3654          * Fire before toggle the menu
3655          * @param {Roo.EventObject} e
3656          */
3657         "beforetoggle" : true
3658     });
3659 };
3660
3661 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3662     
3663     
3664    
3665     // private
3666     navItems : false,
3667     loadMask : false,
3668     
3669     
3670     getAutoCreate : function(){
3671         
3672         
3673         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3674         
3675     },
3676     
3677     initEvents :function ()
3678     {
3679         //Roo.log(this.el.select('.navbar-toggle',true));
3680         this.el.select('.navbar-toggle',true).on('click', function() {
3681             if(this.fireEvent('beforetoggle', this) !== false){
3682                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3683             }
3684             
3685         }, this);
3686         
3687         var mark = {
3688             tag: "div",
3689             cls:"x-dlg-mask"
3690         };
3691         
3692         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3693         
3694         var size = this.el.getSize();
3695         this.maskEl.setSize(size.width, size.height);
3696         this.maskEl.enableDisplayMode("block");
3697         this.maskEl.hide();
3698         
3699         if(this.loadMask){
3700             this.maskEl.show();
3701         }
3702     },
3703     
3704     
3705     getChildContainer : function()
3706     {
3707         if (this.el.select('.collapse').getCount()) {
3708             return this.el.select('.collapse',true).first();
3709         }
3710         
3711         return this.el;
3712     },
3713     
3714     mask : function()
3715     {
3716         this.maskEl.show();
3717     },
3718     
3719     unmask : function()
3720     {
3721         this.maskEl.hide();
3722     } 
3723     
3724     
3725     
3726     
3727 });
3728
3729
3730
3731  
3732
3733  /*
3734  * - LGPL
3735  *
3736  * navbar
3737  * 
3738  */
3739
3740 /**
3741  * @class Roo.bootstrap.NavSimplebar
3742  * @extends Roo.bootstrap.Navbar
3743  * Bootstrap Sidebar class
3744  *
3745  * @cfg {Boolean} inverse is inverted color
3746  * 
3747  * @cfg {String} type (nav | pills | tabs)
3748  * @cfg {Boolean} arrangement stacked | justified
3749  * @cfg {String} align (left | right) alignment
3750  * 
3751  * @cfg {Boolean} main (true|false) main nav bar? default false
3752  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3753  * 
3754  * @cfg {String} tag (header|footer|nav|div) default is nav 
3755
3756  * 
3757  * 
3758  * 
3759  * @constructor
3760  * Create a new Sidebar
3761  * @param {Object} config The config object
3762  */
3763
3764
3765 Roo.bootstrap.NavSimplebar = function(config){
3766     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3767 };
3768
3769 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3770     
3771     inverse: false,
3772     
3773     type: false,
3774     arrangement: '',
3775     align : false,
3776     
3777     
3778     
3779     main : false,
3780     
3781     
3782     tag : false,
3783     
3784     
3785     getAutoCreate : function(){
3786         
3787         
3788         var cfg = {
3789             tag : this.tag || 'div',
3790             cls : 'navbar'
3791         };
3792           
3793         
3794         cfg.cn = [
3795             {
3796                 cls: 'nav',
3797                 tag : 'ul'
3798             }
3799         ];
3800         
3801          
3802         this.type = this.type || 'nav';
3803         if (['tabs','pills'].indexOf(this.type)!==-1) {
3804             cfg.cn[0].cls += ' nav-' + this.type
3805         
3806         
3807         } else {
3808             if (this.type!=='nav') {
3809                 Roo.log('nav type must be nav/tabs/pills')
3810             }
3811             cfg.cn[0].cls += ' navbar-nav'
3812         }
3813         
3814         
3815         
3816         
3817         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3818             cfg.cn[0].cls += ' nav-' + this.arrangement;
3819         }
3820         
3821         
3822         if (this.align === 'right') {
3823             cfg.cn[0].cls += ' navbar-right';
3824         }
3825         
3826         if (this.inverse) {
3827             cfg.cls += ' navbar-inverse';
3828             
3829         }
3830         
3831         
3832         return cfg;
3833     
3834         
3835     }
3836     
3837     
3838     
3839 });
3840
3841
3842
3843  
3844
3845  
3846        /*
3847  * - LGPL
3848  *
3849  * navbar
3850  * 
3851  */
3852
3853 /**
3854  * @class Roo.bootstrap.NavHeaderbar
3855  * @extends Roo.bootstrap.NavSimplebar
3856  * Bootstrap Sidebar class
3857  *
3858  * @cfg {String} brand what is brand
3859  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3860  * @cfg {String} brand_href href of the brand
3861  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3862  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3863  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3864  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3865  * 
3866  * @constructor
3867  * Create a new Sidebar
3868  * @param {Object} config The config object
3869  */
3870
3871
3872 Roo.bootstrap.NavHeaderbar = function(config){
3873     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3874       
3875 };
3876
3877 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3878     
3879     position: '',
3880     brand: '',
3881     brand_href: false,
3882     srButton : true,
3883     autohide : false,
3884     desktopCenter : false,
3885    
3886     
3887     getAutoCreate : function(){
3888         
3889         var   cfg = {
3890             tag: this.nav || 'nav',
3891             cls: 'navbar',
3892             role: 'navigation',
3893             cn: []
3894         };
3895         
3896         var cn = cfg.cn;
3897         if (this.desktopCenter) {
3898             cn.push({cls : 'container', cn : []});
3899             cn = cn[0].cn;
3900         }
3901         
3902         if(this.srButton){
3903             cn.push({
3904                 tag: 'div',
3905                 cls: 'navbar-header',
3906                 cn: [
3907                     {
3908                         tag: 'button',
3909                         type: 'button',
3910                         cls: 'navbar-toggle',
3911                         'data-toggle': 'collapse',
3912                         cn: [
3913                             {
3914                                 tag: 'span',
3915                                 cls: 'sr-only',
3916                                 html: 'Toggle navigation'
3917                             },
3918                             {
3919                                 tag: 'span',
3920                                 cls: 'icon-bar'
3921                             },
3922                             {
3923                                 tag: 'span',
3924                                 cls: 'icon-bar'
3925                             },
3926                             {
3927                                 tag: 'span',
3928                                 cls: 'icon-bar'
3929                             }
3930                         ]
3931                     }
3932                 ]
3933             });
3934         }
3935         
3936         cn.push({
3937             tag: 'div',
3938             cls: 'collapse navbar-collapse',
3939             cn : []
3940         });
3941         
3942         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3943         
3944         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3945             cfg.cls += ' navbar-' + this.position;
3946             
3947             // tag can override this..
3948             
3949             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3950         }
3951         
3952         if (this.brand !== '') {
3953             cn[0].cn.push({
3954                 tag: 'a',
3955                 href: this.brand_href ? this.brand_href : '#',
3956                 cls: 'navbar-brand',
3957                 cn: [
3958                 this.brand
3959                 ]
3960             });
3961         }
3962         
3963         if(this.main){
3964             cfg.cls += ' main-nav';
3965         }
3966         
3967         
3968         return cfg;
3969
3970         
3971     },
3972     getHeaderChildContainer : function()
3973     {
3974         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3975             return this.el.select('.navbar-header',true).first();
3976         }
3977         
3978         return this.getChildContainer();
3979     },
3980     
3981     
3982     initEvents : function()
3983     {
3984         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3985         
3986         if (this.autohide) {
3987             
3988             var prevScroll = 0;
3989             var ft = this.el;
3990             
3991             Roo.get(document).on('scroll',function(e) {
3992                 var ns = Roo.get(document).getScroll().top;
3993                 var os = prevScroll;
3994                 prevScroll = ns;
3995                 
3996                 if(ns > os){
3997                     ft.removeClass('slideDown');
3998                     ft.addClass('slideUp');
3999                     return;
4000                 }
4001                 ft.removeClass('slideUp');
4002                 ft.addClass('slideDown');
4003                  
4004               
4005           },this);
4006         }
4007     }    
4008     
4009 });
4010
4011
4012
4013  
4014
4015  /*
4016  * - LGPL
4017  *
4018  * navbar
4019  * 
4020  */
4021
4022 /**
4023  * @class Roo.bootstrap.NavSidebar
4024  * @extends Roo.bootstrap.Navbar
4025  * Bootstrap Sidebar class
4026  * 
4027  * @constructor
4028  * Create a new Sidebar
4029  * @param {Object} config The config object
4030  */
4031
4032
4033 Roo.bootstrap.NavSidebar = function(config){
4034     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4035 };
4036
4037 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4038     
4039     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4040     
4041     getAutoCreate : function(){
4042         
4043         
4044         return  {
4045             tag: 'div',
4046             cls: 'sidebar sidebar-nav'
4047         };
4048     
4049         
4050     }
4051     
4052     
4053     
4054 });
4055
4056
4057
4058  
4059
4060  /*
4061  * - LGPL
4062  *
4063  * nav group
4064  * 
4065  */
4066
4067 /**
4068  * @class Roo.bootstrap.NavGroup
4069  * @extends Roo.bootstrap.Component
4070  * Bootstrap NavGroup class
4071  * @cfg {String} align (left|right)
4072  * @cfg {Boolean} inverse
4073  * @cfg {String} type (nav|pills|tab) default nav
4074  * @cfg {String} navId - reference Id for navbar.
4075
4076  * 
4077  * @constructor
4078  * Create a new nav group
4079  * @param {Object} config The config object
4080  */
4081
4082 Roo.bootstrap.NavGroup = function(config){
4083     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4084     this.navItems = [];
4085    
4086     Roo.bootstrap.NavGroup.register(this);
4087      this.addEvents({
4088         /**
4089              * @event changed
4090              * Fires when the active item changes
4091              * @param {Roo.bootstrap.NavGroup} this
4092              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4093              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4094          */
4095         'changed': true
4096      });
4097     
4098 };
4099
4100 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4101     
4102     align: '',
4103     inverse: false,
4104     form: false,
4105     type: 'nav',
4106     navId : '',
4107     // private
4108     
4109     navItems : false, 
4110     
4111     getAutoCreate : function()
4112     {
4113         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4114         
4115         cfg = {
4116             tag : 'ul',
4117             cls: 'nav' 
4118         };
4119         
4120         if (['tabs','pills'].indexOf(this.type)!==-1) {
4121             cfg.cls += ' nav-' + this.type
4122         } else {
4123             if (this.type!=='nav') {
4124                 Roo.log('nav type must be nav/tabs/pills')
4125             }
4126             cfg.cls += ' navbar-nav'
4127         }
4128         
4129         if (this.parent() && this.parent().sidebar) {
4130             cfg = {
4131                 tag: 'ul',
4132                 cls: 'dashboard-menu sidebar-menu'
4133             };
4134             
4135             return cfg;
4136         }
4137         
4138         if (this.form === true) {
4139             cfg = {
4140                 tag: 'form',
4141                 cls: 'navbar-form'
4142             };
4143             
4144             if (this.align === 'right') {
4145                 cfg.cls += ' navbar-right';
4146             } else {
4147                 cfg.cls += ' navbar-left';
4148             }
4149         }
4150         
4151         if (this.align === 'right') {
4152             cfg.cls += ' navbar-right';
4153         }
4154         
4155         if (this.inverse) {
4156             cfg.cls += ' navbar-inverse';
4157             
4158         }
4159         
4160         
4161         return cfg;
4162     },
4163     /**
4164     * sets the active Navigation item
4165     * @param {Roo.bootstrap.NavItem} the new current navitem
4166     */
4167     setActiveItem : function(item)
4168     {
4169         var prev = false;
4170         Roo.each(this.navItems, function(v){
4171             if (v == item) {
4172                 return ;
4173             }
4174             if (v.isActive()) {
4175                 v.setActive(false, true);
4176                 prev = v;
4177                 
4178             }
4179             
4180         });
4181
4182         item.setActive(true, true);
4183         this.fireEvent('changed', this, item, prev);
4184         
4185         
4186     },
4187     /**
4188     * gets the active Navigation item
4189     * @return {Roo.bootstrap.NavItem} the current navitem
4190     */
4191     getActive : function()
4192     {
4193         
4194         var prev = false;
4195         Roo.each(this.navItems, function(v){
4196             
4197             if (v.isActive()) {
4198                 prev = v;
4199                 
4200             }
4201             
4202         });
4203         return prev;
4204     },
4205     
4206     indexOfNav : function()
4207     {
4208         
4209         var prev = false;
4210         Roo.each(this.navItems, function(v,i){
4211             
4212             if (v.isActive()) {
4213                 prev = i;
4214                 
4215             }
4216             
4217         });
4218         return prev;
4219     },
4220     /**
4221     * adds a Navigation item
4222     * @param {Roo.bootstrap.NavItem} the navitem to add
4223     */
4224     addItem : function(cfg)
4225     {
4226         var cn = new Roo.bootstrap.NavItem(cfg);
4227         this.register(cn);
4228         cn.parentId = this.id;
4229         cn.onRender(this.el, null);
4230         return cn;
4231     },
4232     /**
4233     * register a Navigation item
4234     * @param {Roo.bootstrap.NavItem} the navitem to add
4235     */
4236     register : function(item)
4237     {
4238         this.navItems.push( item);
4239         item.navId = this.navId;
4240     
4241     },
4242     
4243     /**
4244     * clear all the Navigation item
4245     */
4246    
4247     clearAll : function()
4248     {
4249         this.navItems = [];
4250         this.el.dom.innerHTML = '';
4251     },
4252     
4253     getNavItem: function(tabId)
4254     {
4255         var ret = false;
4256         Roo.each(this.navItems, function(e) {
4257             if (e.tabId == tabId) {
4258                ret =  e;
4259                return false;
4260             }
4261             return true;
4262             
4263         });
4264         return ret;
4265     },
4266     
4267     setActiveNext : function()
4268     {
4269         var i = this.indexOfNav(this.getActive());
4270         if (i > this.navItems.length) {
4271             return;
4272         }
4273         this.setActiveItem(this.navItems[i+1]);
4274     },
4275     setActivePrev : function()
4276     {
4277         var i = this.indexOfNav(this.getActive());
4278         if (i  < 1) {
4279             return;
4280         }
4281         this.setActiveItem(this.navItems[i-1]);
4282     },
4283     clearWasActive : function(except) {
4284         Roo.each(this.navItems, function(e) {
4285             if (e.tabId != except.tabId && e.was_active) {
4286                e.was_active = false;
4287                return false;
4288             }
4289             return true;
4290             
4291         });
4292     },
4293     getWasActive : function ()
4294     {
4295         var r = false;
4296         Roo.each(this.navItems, function(e) {
4297             if (e.was_active) {
4298                r = e;
4299                return false;
4300             }
4301             return true;
4302             
4303         });
4304         return r;
4305     }
4306     
4307     
4308 });
4309
4310  
4311 Roo.apply(Roo.bootstrap.NavGroup, {
4312     
4313     groups: {},
4314      /**
4315     * register a Navigation Group
4316     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4317     */
4318     register : function(navgrp)
4319     {
4320         this.groups[navgrp.navId] = navgrp;
4321         
4322     },
4323     /**
4324     * fetch a Navigation Group based on the navigation ID
4325     * @param {string} the navgroup to add
4326     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4327     */
4328     get: function(navId) {
4329         if (typeof(this.groups[navId]) == 'undefined') {
4330             return false;
4331             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4332         }
4333         return this.groups[navId] ;
4334     }
4335     
4336     
4337     
4338 });
4339
4340  /*
4341  * - LGPL
4342  *
4343  * row
4344  * 
4345  */
4346
4347 /**
4348  * @class Roo.bootstrap.NavItem
4349  * @extends Roo.bootstrap.Component
4350  * Bootstrap Navbar.NavItem class
4351  * @cfg {String} href  link to
4352  * @cfg {String} html content of button
4353  * @cfg {String} badge text inside badge
4354  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4355  * @cfg {String} glyphicon name of glyphicon
4356  * @cfg {String} icon name of font awesome icon
4357  * @cfg {Boolean} active Is item active
4358  * @cfg {Boolean} disabled Is item disabled
4359  
4360  * @cfg {Boolean} preventDefault (true | false) default false
4361  * @cfg {String} tabId the tab that this item activates.
4362  * @cfg {String} tagtype (a|span) render as a href or span?
4363  * @cfg {Boolean} animateRef (true|false) link to element default false  
4364   
4365  * @constructor
4366  * Create a new Navbar Item
4367  * @param {Object} config The config object
4368  */
4369 Roo.bootstrap.NavItem = function(config){
4370     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4371     this.addEvents({
4372         // raw events
4373         /**
4374          * @event click
4375          * The raw click event for the entire grid.
4376          * @param {Roo.EventObject} e
4377          */
4378         "click" : true,
4379          /**
4380             * @event changed
4381             * Fires when the active item active state changes
4382             * @param {Roo.bootstrap.NavItem} this
4383             * @param {boolean} state the new state
4384              
4385          */
4386         'changed': true,
4387         /**
4388             * @event scrollto
4389             * Fires when scroll to element
4390             * @param {Roo.bootstrap.NavItem} this
4391             * @param {Object} options
4392             * @param {Roo.EventObject} e
4393              
4394          */
4395         'scrollto': true
4396     });
4397    
4398 };
4399
4400 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4401     
4402     href: false,
4403     html: '',
4404     badge: '',
4405     icon: false,
4406     glyphicon: false,
4407     active: false,
4408     preventDefault : false,
4409     tabId : false,
4410     tagtype : 'a',
4411     disabled : false,
4412     animateRef : false,
4413     was_active : false,
4414     
4415     getAutoCreate : function(){
4416          
4417         var cfg = {
4418             tag: 'li',
4419             cls: 'nav-item'
4420             
4421         };
4422         
4423         if (this.active) {
4424             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4425         }
4426         if (this.disabled) {
4427             cfg.cls += ' disabled';
4428         }
4429         
4430         if (this.href || this.html || this.glyphicon || this.icon) {
4431             cfg.cn = [
4432                 {
4433                     tag: this.tagtype,
4434                     href : this.href || "#",
4435                     html: this.html || ''
4436                 }
4437             ];
4438             
4439             if (this.icon) {
4440                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4441             }
4442
4443             if(this.glyphicon) {
4444                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4445             }
4446             
4447             if (this.menu) {
4448                 
4449                 cfg.cn[0].html += " <span class='caret'></span>";
4450              
4451             }
4452             
4453             if (this.badge !== '') {
4454                  
4455                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4456             }
4457         }
4458         
4459         
4460         
4461         return cfg;
4462     },
4463     initEvents: function() 
4464     {
4465         if (typeof (this.menu) != 'undefined') {
4466             this.menu.parentType = this.xtype;
4467             this.menu.triggerEl = this.el;
4468             this.menu = this.addxtype(Roo.apply({}, this.menu));
4469         }
4470         
4471         this.el.select('a',true).on('click', this.onClick, this);
4472         
4473         if(this.tagtype == 'span'){
4474             this.el.select('span',true).on('click', this.onClick, this);
4475         }
4476        
4477         // at this point parent should be available..
4478         this.parent().register(this);
4479     },
4480     
4481     onClick : function(e)
4482     {
4483         if (e.getTarget('.dropdown-menu-item')) {
4484             // did you click on a menu itemm.... - then don't trigger onclick..
4485             return;
4486         }
4487         
4488         if(
4489                 this.preventDefault || 
4490                 this.href == '#' 
4491         ){
4492             Roo.log("NavItem - prevent Default?");
4493             e.preventDefault();
4494         }
4495         
4496         if (this.disabled) {
4497             return;
4498         }
4499         
4500         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4501         if (tg && tg.transition) {
4502             Roo.log("waiting for the transitionend");
4503             return;
4504         }
4505         
4506         
4507         
4508         //Roo.log("fire event clicked");
4509         if(this.fireEvent('click', this, e) === false){
4510             return;
4511         };
4512         
4513         if(this.tagtype == 'span'){
4514             return;
4515         }
4516         
4517         //Roo.log(this.href);
4518         var ael = this.el.select('a',true).first();
4519         //Roo.log(ael);
4520         
4521         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4522             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4523             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4524                 return; // ignore... - it's a 'hash' to another page.
4525             }
4526             Roo.log("NavItem - prevent Default?");
4527             e.preventDefault();
4528             this.scrollToElement(e);
4529         }
4530         
4531         
4532         var p =  this.parent();
4533    
4534         if (['tabs','pills'].indexOf(p.type)!==-1) {
4535             if (typeof(p.setActiveItem) !== 'undefined') {
4536                 p.setActiveItem(this);
4537             }
4538         }
4539         
4540         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4541         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4542             // remove the collapsed menu expand...
4543             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4544         }
4545     },
4546     
4547     isActive: function () {
4548         return this.active
4549     },
4550     setActive : function(state, fire, is_was_active)
4551     {
4552         if (this.active && !state && this.navId) {
4553             this.was_active = true;
4554             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4555             if (nv) {
4556                 nv.clearWasActive(this);
4557             }
4558             
4559         }
4560         this.active = state;
4561         
4562         if (!state ) {
4563             this.el.removeClass('active');
4564         } else if (!this.el.hasClass('active')) {
4565             this.el.addClass('active');
4566         }
4567         if (fire) {
4568             this.fireEvent('changed', this, state);
4569         }
4570         
4571         // show a panel if it's registered and related..
4572         
4573         if (!this.navId || !this.tabId || !state || is_was_active) {
4574             return;
4575         }
4576         
4577         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4578         if (!tg) {
4579             return;
4580         }
4581         var pan = tg.getPanelByName(this.tabId);
4582         if (!pan) {
4583             return;
4584         }
4585         // if we can not flip to new panel - go back to old nav highlight..
4586         if (false == tg.showPanel(pan)) {
4587             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4588             if (nv) {
4589                 var onav = nv.getWasActive();
4590                 if (onav) {
4591                     onav.setActive(true, false, true);
4592                 }
4593             }
4594             
4595         }
4596         
4597         
4598         
4599     },
4600      // this should not be here...
4601     setDisabled : function(state)
4602     {
4603         this.disabled = state;
4604         if (!state ) {
4605             this.el.removeClass('disabled');
4606         } else if (!this.el.hasClass('disabled')) {
4607             this.el.addClass('disabled');
4608         }
4609         
4610     },
4611     
4612     /**
4613      * Fetch the element to display the tooltip on.
4614      * @return {Roo.Element} defaults to this.el
4615      */
4616     tooltipEl : function()
4617     {
4618         return this.el.select('' + this.tagtype + '', true).first();
4619     },
4620     
4621     scrollToElement : function(e)
4622     {
4623         var c = document.body;
4624         
4625         /*
4626          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4627          */
4628         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4629             c = document.documentElement;
4630         }
4631         
4632         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4633         
4634         if(!target){
4635             return;
4636         }
4637
4638         var o = target.calcOffsetsTo(c);
4639         
4640         var options = {
4641             target : target,
4642             value : o[1]
4643         };
4644         
4645         this.fireEvent('scrollto', this, options, e);
4646         
4647         Roo.get(c).scrollTo('top', options.value, true);
4648         
4649         return;
4650     }
4651 });
4652  
4653
4654  /*
4655  * - LGPL
4656  *
4657  * sidebar item
4658  *
4659  *  li
4660  *    <span> icon </span>
4661  *    <span> text </span>
4662  *    <span>badge </span>
4663  */
4664
4665 /**
4666  * @class Roo.bootstrap.NavSidebarItem
4667  * @extends Roo.bootstrap.NavItem
4668  * Bootstrap Navbar.NavSidebarItem class
4669  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4670  * {Boolean} open is the menu open
4671  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4672  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4673  * {String} buttonSize (sm|md|lg)the extra classes for the button
4674  * {Boolean} showArrow show arrow next to the text (default true)
4675  * @constructor
4676  * Create a new Navbar Button
4677  * @param {Object} config The config object
4678  */
4679 Roo.bootstrap.NavSidebarItem = function(config){
4680     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4681     this.addEvents({
4682         // raw events
4683         /**
4684          * @event click
4685          * The raw click event for the entire grid.
4686          * @param {Roo.EventObject} e
4687          */
4688         "click" : true,
4689          /**
4690             * @event changed
4691             * Fires when the active item active state changes
4692             * @param {Roo.bootstrap.NavSidebarItem} this
4693             * @param {boolean} state the new state
4694              
4695          */
4696         'changed': true
4697     });
4698    
4699 };
4700
4701 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4702     
4703     badgeWeight : 'default',
4704     
4705     open: false,
4706     
4707     buttonView : false,
4708     
4709     buttonWeight : 'default',
4710     
4711     buttonSize : 'md',
4712     
4713     showArrow : true,
4714     
4715     getAutoCreate : function(){
4716         
4717         
4718         var a = {
4719                 tag: 'a',
4720                 href : this.href || '#',
4721                 cls: '',
4722                 html : '',
4723                 cn : []
4724         };
4725         
4726         if(this.buttonView){
4727             a = {
4728                 tag: 'button',
4729                 href : this.href || '#',
4730                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4731                 html : this.html,
4732                 cn : []
4733             };
4734         }
4735         
4736         var cfg = {
4737             tag: 'li',
4738             cls: '',
4739             cn: [ a ]
4740         };
4741         
4742         if (this.active) {
4743             cfg.cls += ' active';
4744         }
4745         
4746         if (this.disabled) {
4747             cfg.cls += ' disabled';
4748         }
4749         if (this.open) {
4750             cfg.cls += ' open x-open';
4751         }
4752         // left icon..
4753         if (this.glyphicon || this.icon) {
4754             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4755             a.cn.push({ tag : 'i', cls : c }) ;
4756         }
4757         
4758         if(!this.buttonView){
4759             var span = {
4760                 tag: 'span',
4761                 html : this.html || ''
4762             };
4763
4764             a.cn.push(span);
4765             
4766         }
4767         
4768         if (this.badge !== '') {
4769             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4770         }
4771         
4772         if (this.menu) {
4773             
4774             if(this.showArrow){
4775                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4776             }
4777             
4778             a.cls += ' dropdown-toggle treeview' ;
4779         }
4780         
4781         return cfg;
4782     },
4783     
4784     initEvents : function()
4785     { 
4786         if (typeof (this.menu) != 'undefined') {
4787             this.menu.parentType = this.xtype;
4788             this.menu.triggerEl = this.el;
4789             this.menu = this.addxtype(Roo.apply({}, this.menu));
4790         }
4791         
4792         this.el.on('click', this.onClick, this);
4793         
4794         if(this.badge !== ''){
4795             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4796         }
4797         
4798     },
4799     
4800     onClick : function(e)
4801     {
4802         if(this.disabled){
4803             e.preventDefault();
4804             return;
4805         }
4806         
4807         if(this.preventDefault){
4808             e.preventDefault();
4809         }
4810         
4811         this.fireEvent('click', this);
4812     },
4813     
4814     disable : function()
4815     {
4816         this.setDisabled(true);
4817     },
4818     
4819     enable : function()
4820     {
4821         this.setDisabled(false);
4822     },
4823     
4824     setDisabled : function(state)
4825     {
4826         if(this.disabled == state){
4827             return;
4828         }
4829         
4830         this.disabled = state;
4831         
4832         if (state) {
4833             this.el.addClass('disabled');
4834             return;
4835         }
4836         
4837         this.el.removeClass('disabled');
4838         
4839         return;
4840     },
4841     
4842     setActive : function(state)
4843     {
4844         if(this.active == state){
4845             return;
4846         }
4847         
4848         this.active = state;
4849         
4850         if (state) {
4851             this.el.addClass('active');
4852             return;
4853         }
4854         
4855         this.el.removeClass('active');
4856         
4857         return;
4858     },
4859     
4860     isActive: function () 
4861     {
4862         return this.active;
4863     },
4864     
4865     setBadge : function(str)
4866     {
4867         if(!this.badgeEl){
4868             return;
4869         }
4870         
4871         this.badgeEl.dom.innerHTML = str;
4872     }
4873     
4874    
4875      
4876  
4877 });
4878  
4879
4880  /*
4881  * - LGPL
4882  *
4883  * row
4884  * 
4885  */
4886
4887 /**
4888  * @class Roo.bootstrap.Row
4889  * @extends Roo.bootstrap.Component
4890  * Bootstrap Row class (contains columns...)
4891  * 
4892  * @constructor
4893  * Create a new Row
4894  * @param {Object} config The config object
4895  */
4896
4897 Roo.bootstrap.Row = function(config){
4898     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4899 };
4900
4901 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4902     
4903     getAutoCreate : function(){
4904        return {
4905             cls: 'row clearfix'
4906        };
4907     }
4908     
4909     
4910 });
4911
4912  
4913
4914  /*
4915  * - LGPL
4916  *
4917  * element
4918  * 
4919  */
4920
4921 /**
4922  * @class Roo.bootstrap.Element
4923  * @extends Roo.bootstrap.Component
4924  * Bootstrap Element class
4925  * @cfg {String} html contents of the element
4926  * @cfg {String} tag tag of the element
4927  * @cfg {String} cls class of the element
4928  * @cfg {Boolean} preventDefault (true|false) default false
4929  * @cfg {Boolean} clickable (true|false) default false
4930  * 
4931  * @constructor
4932  * Create a new Element
4933  * @param {Object} config The config object
4934  */
4935
4936 Roo.bootstrap.Element = function(config){
4937     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4938     
4939     this.addEvents({
4940         // raw events
4941         /**
4942          * @event click
4943          * When a element is chick
4944          * @param {Roo.bootstrap.Element} this
4945          * @param {Roo.EventObject} e
4946          */
4947         "click" : true
4948     });
4949 };
4950
4951 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4952     
4953     tag: 'div',
4954     cls: '',
4955     html: '',
4956     preventDefault: false, 
4957     clickable: false,
4958     
4959     getAutoCreate : function(){
4960         
4961         var cfg = {
4962             tag: this.tag,
4963             // cls: this.cls, double assign in parent class Component.js :: onRender
4964             html: this.html
4965         };
4966         
4967         return cfg;
4968     },
4969     
4970     initEvents: function() 
4971     {
4972         Roo.bootstrap.Element.superclass.initEvents.call(this);
4973         
4974         if(this.clickable){
4975             this.el.on('click', this.onClick, this);
4976         }
4977         
4978     },
4979     
4980     onClick : function(e)
4981     {
4982         if(this.preventDefault){
4983             e.preventDefault();
4984         }
4985         
4986         this.fireEvent('click', this, e);
4987     },
4988     
4989     getValue : function()
4990     {
4991         return this.el.dom.innerHTML;
4992     },
4993     
4994     setValue : function(value)
4995     {
4996         this.el.dom.innerHTML = value;
4997     }
4998    
4999 });
5000
5001  
5002
5003  /*
5004  * - LGPL
5005  *
5006  * pagination
5007  * 
5008  */
5009
5010 /**
5011  * @class Roo.bootstrap.Pagination
5012  * @extends Roo.bootstrap.Component
5013  * Bootstrap Pagination class
5014  * @cfg {String} size xs | sm | md | lg
5015  * @cfg {Boolean} inverse false | true
5016  * 
5017  * @constructor
5018  * Create a new Pagination
5019  * @param {Object} config The config object
5020  */
5021
5022 Roo.bootstrap.Pagination = function(config){
5023     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5024 };
5025
5026 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5027     
5028     cls: false,
5029     size: false,
5030     inverse: false,
5031     
5032     getAutoCreate : function(){
5033         var cfg = {
5034             tag: 'ul',
5035                 cls: 'pagination'
5036         };
5037         if (this.inverse) {
5038             cfg.cls += ' inverse';
5039         }
5040         if (this.html) {
5041             cfg.html=this.html;
5042         }
5043         if (this.cls) {
5044             cfg.cls += " " + this.cls;
5045         }
5046         return cfg;
5047     }
5048    
5049 });
5050
5051  
5052
5053  /*
5054  * - LGPL
5055  *
5056  * Pagination item
5057  * 
5058  */
5059
5060
5061 /**
5062  * @class Roo.bootstrap.PaginationItem
5063  * @extends Roo.bootstrap.Component
5064  * Bootstrap PaginationItem class
5065  * @cfg {String} html text
5066  * @cfg {String} href the link
5067  * @cfg {Boolean} preventDefault (true | false) default true
5068  * @cfg {Boolean} active (true | false) default false
5069  * @cfg {Boolean} disabled default false
5070  * 
5071  * 
5072  * @constructor
5073  * Create a new PaginationItem
5074  * @param {Object} config The config object
5075  */
5076
5077
5078 Roo.bootstrap.PaginationItem = function(config){
5079     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5080     this.addEvents({
5081         // raw events
5082         /**
5083          * @event click
5084          * The raw click event for the entire grid.
5085          * @param {Roo.EventObject} e
5086          */
5087         "click" : true
5088     });
5089 };
5090
5091 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5092     
5093     href : false,
5094     html : false,
5095     preventDefault: true,
5096     active : false,
5097     cls : false,
5098     disabled: false,
5099     
5100     getAutoCreate : function(){
5101         var cfg= {
5102             tag: 'li',
5103             cn: [
5104                 {
5105                     tag : 'a',
5106                     href : this.href ? this.href : '#',
5107                     html : this.html ? this.html : ''
5108                 }
5109             ]
5110         };
5111         
5112         if(this.cls){
5113             cfg.cls = this.cls;
5114         }
5115         
5116         if(this.disabled){
5117             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5118         }
5119         
5120         if(this.active){
5121             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5122         }
5123         
5124         return cfg;
5125     },
5126     
5127     initEvents: function() {
5128         
5129         this.el.on('click', this.onClick, this);
5130         
5131     },
5132     onClick : function(e)
5133     {
5134         Roo.log('PaginationItem on click ');
5135         if(this.preventDefault){
5136             e.preventDefault();
5137         }
5138         
5139         if(this.disabled){
5140             return;
5141         }
5142         
5143         this.fireEvent('click', this, e);
5144     }
5145    
5146 });
5147
5148  
5149
5150  /*
5151  * - LGPL
5152  *
5153  * slider
5154  * 
5155  */
5156
5157
5158 /**
5159  * @class Roo.bootstrap.Slider
5160  * @extends Roo.bootstrap.Component
5161  * Bootstrap Slider class
5162  *    
5163  * @constructor
5164  * Create a new Slider
5165  * @param {Object} config The config object
5166  */
5167
5168 Roo.bootstrap.Slider = function(config){
5169     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5170 };
5171
5172 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5173     
5174     getAutoCreate : function(){
5175         
5176         var cfg = {
5177             tag: 'div',
5178             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5179             cn: [
5180                 {
5181                     tag: 'a',
5182                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5183                 }
5184             ]
5185         };
5186         
5187         return cfg;
5188     }
5189    
5190 });
5191
5192  /*
5193  * Based on:
5194  * Ext JS Library 1.1.1
5195  * Copyright(c) 2006-2007, Ext JS, LLC.
5196  *
5197  * Originally Released Under LGPL - original licence link has changed is not relivant.
5198  *
5199  * Fork - LGPL
5200  * <script type="text/javascript">
5201  */
5202  
5203
5204 /**
5205  * @class Roo.grid.ColumnModel
5206  * @extends Roo.util.Observable
5207  * This is the default implementation of a ColumnModel used by the Grid. It defines
5208  * the columns in the grid.
5209  * <br>Usage:<br>
5210  <pre><code>
5211  var colModel = new Roo.grid.ColumnModel([
5212         {header: "Ticker", width: 60, sortable: true, locked: true},
5213         {header: "Company Name", width: 150, sortable: true},
5214         {header: "Market Cap.", width: 100, sortable: true},
5215         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5216         {header: "Employees", width: 100, sortable: true, resizable: false}
5217  ]);
5218  </code></pre>
5219  * <p>
5220  
5221  * The config options listed for this class are options which may appear in each
5222  * individual column definition.
5223  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5224  * @constructor
5225  * @param {Object} config An Array of column config objects. See this class's
5226  * config objects for details.
5227 */
5228 Roo.grid.ColumnModel = function(config){
5229         /**
5230      * The config passed into the constructor
5231      */
5232     this.config = config;
5233     this.lookup = {};
5234
5235     // if no id, create one
5236     // if the column does not have a dataIndex mapping,
5237     // map it to the order it is in the config
5238     for(var i = 0, len = config.length; i < len; i++){
5239         var c = config[i];
5240         if(typeof c.dataIndex == "undefined"){
5241             c.dataIndex = i;
5242         }
5243         if(typeof c.renderer == "string"){
5244             c.renderer = Roo.util.Format[c.renderer];
5245         }
5246         if(typeof c.id == "undefined"){
5247             c.id = Roo.id();
5248         }
5249         if(c.editor && c.editor.xtype){
5250             c.editor  = Roo.factory(c.editor, Roo.grid);
5251         }
5252         if(c.editor && c.editor.isFormField){
5253             c.editor = new Roo.grid.GridEditor(c.editor);
5254         }
5255         this.lookup[c.id] = c;
5256     }
5257
5258     /**
5259      * The width of columns which have no width specified (defaults to 100)
5260      * @type Number
5261      */
5262     this.defaultWidth = 100;
5263
5264     /**
5265      * Default sortable of columns which have no sortable specified (defaults to false)
5266      * @type Boolean
5267      */
5268     this.defaultSortable = false;
5269
5270     this.addEvents({
5271         /**
5272              * @event widthchange
5273              * Fires when the width of a column changes.
5274              * @param {ColumnModel} this
5275              * @param {Number} columnIndex The column index
5276              * @param {Number} newWidth The new width
5277              */
5278             "widthchange": true,
5279         /**
5280              * @event headerchange
5281              * Fires when the text of a header changes.
5282              * @param {ColumnModel} this
5283              * @param {Number} columnIndex The column index
5284              * @param {Number} newText The new header text
5285              */
5286             "headerchange": true,
5287         /**
5288              * @event hiddenchange
5289              * Fires when a column is hidden or "unhidden".
5290              * @param {ColumnModel} this
5291              * @param {Number} columnIndex The column index
5292              * @param {Boolean} hidden true if hidden, false otherwise
5293              */
5294             "hiddenchange": true,
5295             /**
5296          * @event columnmoved
5297          * Fires when a column is moved.
5298          * @param {ColumnModel} this
5299          * @param {Number} oldIndex
5300          * @param {Number} newIndex
5301          */
5302         "columnmoved" : true,
5303         /**
5304          * @event columlockchange
5305          * Fires when a column's locked state is changed
5306          * @param {ColumnModel} this
5307          * @param {Number} colIndex
5308          * @param {Boolean} locked true if locked
5309          */
5310         "columnlockchange" : true
5311     });
5312     Roo.grid.ColumnModel.superclass.constructor.call(this);
5313 };
5314 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5315     /**
5316      * @cfg {String} header The header text to display in the Grid view.
5317      */
5318     /**
5319      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5320      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5321      * specified, the column's index is used as an index into the Record's data Array.
5322      */
5323     /**
5324      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5325      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5326      */
5327     /**
5328      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5329      * Defaults to the value of the {@link #defaultSortable} property.
5330      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5331      */
5332     /**
5333      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5334      */
5335     /**
5336      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5337      */
5338     /**
5339      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5340      */
5341     /**
5342      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5343      */
5344     /**
5345      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5346      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5347      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5348      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5349      */
5350        /**
5351      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5352      */
5353     /**
5354      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5355      */
5356     /**
5357      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5358      */
5359     /**
5360      * @cfg {String} cursor (Optional)
5361      */
5362     /**
5363      * @cfg {String} tooltip (Optional)
5364      */
5365     /**
5366      * @cfg {Number} xs (Optional)
5367      */
5368     /**
5369      * @cfg {Number} sm (Optional)
5370      */
5371     /**
5372      * @cfg {Number} md (Optional)
5373      */
5374     /**
5375      * @cfg {Number} lg (Optional)
5376      */
5377     /**
5378      * Returns the id of the column at the specified index.
5379      * @param {Number} index The column index
5380      * @return {String} the id
5381      */
5382     getColumnId : function(index){
5383         return this.config[index].id;
5384     },
5385
5386     /**
5387      * Returns the column for a specified id.
5388      * @param {String} id The column id
5389      * @return {Object} the column
5390      */
5391     getColumnById : function(id){
5392         return this.lookup[id];
5393     },
5394
5395     
5396     /**
5397      * Returns the column for a specified dataIndex.
5398      * @param {String} dataIndex The column dataIndex
5399      * @return {Object|Boolean} the column or false if not found
5400      */
5401     getColumnByDataIndex: function(dataIndex){
5402         var index = this.findColumnIndex(dataIndex);
5403         return index > -1 ? this.config[index] : false;
5404     },
5405     
5406     /**
5407      * Returns the index for a specified column id.
5408      * @param {String} id The column id
5409      * @return {Number} the index, or -1 if not found
5410      */
5411     getIndexById : function(id){
5412         for(var i = 0, len = this.config.length; i < len; i++){
5413             if(this.config[i].id == id){
5414                 return i;
5415             }
5416         }
5417         return -1;
5418     },
5419     
5420     /**
5421      * Returns the index for a specified column dataIndex.
5422      * @param {String} dataIndex The column dataIndex
5423      * @return {Number} the index, or -1 if not found
5424      */
5425     
5426     findColumnIndex : function(dataIndex){
5427         for(var i = 0, len = this.config.length; i < len; i++){
5428             if(this.config[i].dataIndex == dataIndex){
5429                 return i;
5430             }
5431         }
5432         return -1;
5433     },
5434     
5435     
5436     moveColumn : function(oldIndex, newIndex){
5437         var c = this.config[oldIndex];
5438         this.config.splice(oldIndex, 1);
5439         this.config.splice(newIndex, 0, c);
5440         this.dataMap = null;
5441         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5442     },
5443
5444     isLocked : function(colIndex){
5445         return this.config[colIndex].locked === true;
5446     },
5447
5448     setLocked : function(colIndex, value, suppressEvent){
5449         if(this.isLocked(colIndex) == value){
5450             return;
5451         }
5452         this.config[colIndex].locked = value;
5453         if(!suppressEvent){
5454             this.fireEvent("columnlockchange", this, colIndex, value);
5455         }
5456     },
5457
5458     getTotalLockedWidth : function(){
5459         var totalWidth = 0;
5460         for(var i = 0; i < this.config.length; i++){
5461             if(this.isLocked(i) && !this.isHidden(i)){
5462                 this.totalWidth += this.getColumnWidth(i);
5463             }
5464         }
5465         return totalWidth;
5466     },
5467
5468     getLockedCount : function(){
5469         for(var i = 0, len = this.config.length; i < len; i++){
5470             if(!this.isLocked(i)){
5471                 return i;
5472             }
5473         }
5474         
5475         return this.config.length;
5476     },
5477
5478     /**
5479      * Returns the number of columns.
5480      * @return {Number}
5481      */
5482     getColumnCount : function(visibleOnly){
5483         if(visibleOnly === true){
5484             var c = 0;
5485             for(var i = 0, len = this.config.length; i < len; i++){
5486                 if(!this.isHidden(i)){
5487                     c++;
5488                 }
5489             }
5490             return c;
5491         }
5492         return this.config.length;
5493     },
5494
5495     /**
5496      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5497      * @param {Function} fn
5498      * @param {Object} scope (optional)
5499      * @return {Array} result
5500      */
5501     getColumnsBy : function(fn, scope){
5502         var r = [];
5503         for(var i = 0, len = this.config.length; i < len; i++){
5504             var c = this.config[i];
5505             if(fn.call(scope||this, c, i) === true){
5506                 r[r.length] = c;
5507             }
5508         }
5509         return r;
5510     },
5511
5512     /**
5513      * Returns true if the specified column is sortable.
5514      * @param {Number} col The column index
5515      * @return {Boolean}
5516      */
5517     isSortable : function(col){
5518         if(typeof this.config[col].sortable == "undefined"){
5519             return this.defaultSortable;
5520         }
5521         return this.config[col].sortable;
5522     },
5523
5524     /**
5525      * Returns the rendering (formatting) function defined for the column.
5526      * @param {Number} col The column index.
5527      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5528      */
5529     getRenderer : function(col){
5530         if(!this.config[col].renderer){
5531             return Roo.grid.ColumnModel.defaultRenderer;
5532         }
5533         return this.config[col].renderer;
5534     },
5535
5536     /**
5537      * Sets the rendering (formatting) function for a column.
5538      * @param {Number} col The column index
5539      * @param {Function} fn The function to use to process the cell's raw data
5540      * to return HTML markup for the grid view. The render function is called with
5541      * the following parameters:<ul>
5542      * <li>Data value.</li>
5543      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5544      * <li>css A CSS style string to apply to the table cell.</li>
5545      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5546      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5547      * <li>Row index</li>
5548      * <li>Column index</li>
5549      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5550      */
5551     setRenderer : function(col, fn){
5552         this.config[col].renderer = fn;
5553     },
5554
5555     /**
5556      * Returns the width for the specified column.
5557      * @param {Number} col The column index
5558      * @return {Number}
5559      */
5560     getColumnWidth : function(col){
5561         return this.config[col].width * 1 || this.defaultWidth;
5562     },
5563
5564     /**
5565      * Sets the width for a column.
5566      * @param {Number} col The column index
5567      * @param {Number} width The new width
5568      */
5569     setColumnWidth : function(col, width, suppressEvent){
5570         this.config[col].width = width;
5571         this.totalWidth = null;
5572         if(!suppressEvent){
5573              this.fireEvent("widthchange", this, col, width);
5574         }
5575     },
5576
5577     /**
5578      * Returns the total width of all columns.
5579      * @param {Boolean} includeHidden True to include hidden column widths
5580      * @return {Number}
5581      */
5582     getTotalWidth : function(includeHidden){
5583         if(!this.totalWidth){
5584             this.totalWidth = 0;
5585             for(var i = 0, len = this.config.length; i < len; i++){
5586                 if(includeHidden || !this.isHidden(i)){
5587                     this.totalWidth += this.getColumnWidth(i);
5588                 }
5589             }
5590         }
5591         return this.totalWidth;
5592     },
5593
5594     /**
5595      * Returns the header for the specified column.
5596      * @param {Number} col The column index
5597      * @return {String}
5598      */
5599     getColumnHeader : function(col){
5600         return this.config[col].header;
5601     },
5602
5603     /**
5604      * Sets the header for a column.
5605      * @param {Number} col The column index
5606      * @param {String} header The new header
5607      */
5608     setColumnHeader : function(col, header){
5609         this.config[col].header = header;
5610         this.fireEvent("headerchange", this, col, header);
5611     },
5612
5613     /**
5614      * Returns the tooltip for the specified column.
5615      * @param {Number} col The column index
5616      * @return {String}
5617      */
5618     getColumnTooltip : function(col){
5619             return this.config[col].tooltip;
5620     },
5621     /**
5622      * Sets the tooltip for a column.
5623      * @param {Number} col The column index
5624      * @param {String} tooltip The new tooltip
5625      */
5626     setColumnTooltip : function(col, tooltip){
5627             this.config[col].tooltip = tooltip;
5628     },
5629
5630     /**
5631      * Returns the dataIndex for the specified column.
5632      * @param {Number} col The column index
5633      * @return {Number}
5634      */
5635     getDataIndex : function(col){
5636         return this.config[col].dataIndex;
5637     },
5638
5639     /**
5640      * Sets the dataIndex for a column.
5641      * @param {Number} col The column index
5642      * @param {Number} dataIndex The new dataIndex
5643      */
5644     setDataIndex : function(col, dataIndex){
5645         this.config[col].dataIndex = dataIndex;
5646     },
5647
5648     
5649     
5650     /**
5651      * Returns true if the cell is editable.
5652      * @param {Number} colIndex The column index
5653      * @param {Number} rowIndex The row index - this is nto actually used..?
5654      * @return {Boolean}
5655      */
5656     isCellEditable : function(colIndex, rowIndex){
5657         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5658     },
5659
5660     /**
5661      * Returns the editor defined for the cell/column.
5662      * return false or null to disable editing.
5663      * @param {Number} colIndex The column index
5664      * @param {Number} rowIndex The row index
5665      * @return {Object}
5666      */
5667     getCellEditor : function(colIndex, rowIndex){
5668         return this.config[colIndex].editor;
5669     },
5670
5671     /**
5672      * Sets if a column is editable.
5673      * @param {Number} col The column index
5674      * @param {Boolean} editable True if the column is editable
5675      */
5676     setEditable : function(col, editable){
5677         this.config[col].editable = editable;
5678     },
5679
5680
5681     /**
5682      * Returns true if the column is hidden.
5683      * @param {Number} colIndex The column index
5684      * @return {Boolean}
5685      */
5686     isHidden : function(colIndex){
5687         return this.config[colIndex].hidden;
5688     },
5689
5690
5691     /**
5692      * Returns true if the column width cannot be changed
5693      */
5694     isFixed : function(colIndex){
5695         return this.config[colIndex].fixed;
5696     },
5697
5698     /**
5699      * Returns true if the column can be resized
5700      * @return {Boolean}
5701      */
5702     isResizable : function(colIndex){
5703         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5704     },
5705     /**
5706      * Sets if a column is hidden.
5707      * @param {Number} colIndex The column index
5708      * @param {Boolean} hidden True if the column is hidden
5709      */
5710     setHidden : function(colIndex, hidden){
5711         this.config[colIndex].hidden = hidden;
5712         this.totalWidth = null;
5713         this.fireEvent("hiddenchange", this, colIndex, hidden);
5714     },
5715
5716     /**
5717      * Sets the editor for a column.
5718      * @param {Number} col The column index
5719      * @param {Object} editor The editor object
5720      */
5721     setEditor : function(col, editor){
5722         this.config[col].editor = editor;
5723     }
5724 });
5725
5726 Roo.grid.ColumnModel.defaultRenderer = function(value)
5727 {
5728     if(typeof value == "object") {
5729         return value;
5730     }
5731         if(typeof value == "string" && value.length < 1){
5732             return "&#160;";
5733         }
5734     
5735         return String.format("{0}", value);
5736 };
5737
5738 // Alias for backwards compatibility
5739 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5740 /*
5741  * Based on:
5742  * Ext JS Library 1.1.1
5743  * Copyright(c) 2006-2007, Ext JS, LLC.
5744  *
5745  * Originally Released Under LGPL - original licence link has changed is not relivant.
5746  *
5747  * Fork - LGPL
5748  * <script type="text/javascript">
5749  */
5750  
5751 /**
5752  * @class Roo.LoadMask
5753  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5754  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5755  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5756  * element's UpdateManager load indicator and will be destroyed after the initial load.
5757  * @constructor
5758  * Create a new LoadMask
5759  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5760  * @param {Object} config The config object
5761  */
5762 Roo.LoadMask = function(el, config){
5763     this.el = Roo.get(el);
5764     Roo.apply(this, config);
5765     if(this.store){
5766         this.store.on('beforeload', this.onBeforeLoad, this);
5767         this.store.on('load', this.onLoad, this);
5768         this.store.on('loadexception', this.onLoadException, this);
5769         this.removeMask = false;
5770     }else{
5771         var um = this.el.getUpdateManager();
5772         um.showLoadIndicator = false; // disable the default indicator
5773         um.on('beforeupdate', this.onBeforeLoad, this);
5774         um.on('update', this.onLoad, this);
5775         um.on('failure', this.onLoad, this);
5776         this.removeMask = true;
5777     }
5778 };
5779
5780 Roo.LoadMask.prototype = {
5781     /**
5782      * @cfg {Boolean} removeMask
5783      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5784      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5785      */
5786     /**
5787      * @cfg {String} msg
5788      * The text to display in a centered loading message box (defaults to 'Loading...')
5789      */
5790     msg : 'Loading...',
5791     /**
5792      * @cfg {String} msgCls
5793      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5794      */
5795     msgCls : 'x-mask-loading',
5796
5797     /**
5798      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5799      * @type Boolean
5800      */
5801     disabled: false,
5802
5803     /**
5804      * Disables the mask to prevent it from being displayed
5805      */
5806     disable : function(){
5807        this.disabled = true;
5808     },
5809
5810     /**
5811      * Enables the mask so that it can be displayed
5812      */
5813     enable : function(){
5814         this.disabled = false;
5815     },
5816     
5817     onLoadException : function()
5818     {
5819         Roo.log(arguments);
5820         
5821         if (typeof(arguments[3]) != 'undefined') {
5822             Roo.MessageBox.alert("Error loading",arguments[3]);
5823         } 
5824         /*
5825         try {
5826             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5827                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5828             }   
5829         } catch(e) {
5830             
5831         }
5832         */
5833     
5834         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5835     },
5836     // private
5837     onLoad : function()
5838     {
5839         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5840     },
5841
5842     // private
5843     onBeforeLoad : function(){
5844         if(!this.disabled){
5845             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5846         }
5847     },
5848
5849     // private
5850     destroy : function(){
5851         if(this.store){
5852             this.store.un('beforeload', this.onBeforeLoad, this);
5853             this.store.un('load', this.onLoad, this);
5854             this.store.un('loadexception', this.onLoadException, this);
5855         }else{
5856             var um = this.el.getUpdateManager();
5857             um.un('beforeupdate', this.onBeforeLoad, this);
5858             um.un('update', this.onLoad, this);
5859             um.un('failure', this.onLoad, this);
5860         }
5861     }
5862 };/*
5863  * - LGPL
5864  *
5865  * table
5866  * 
5867  */
5868
5869 /**
5870  * @class Roo.bootstrap.Table
5871  * @extends Roo.bootstrap.Component
5872  * Bootstrap Table class
5873  * @cfg {String} cls table class
5874  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5875  * @cfg {String} bgcolor Specifies the background color for a table
5876  * @cfg {Number} border Specifies whether the table cells should have borders or not
5877  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5878  * @cfg {Number} cellspacing Specifies the space between cells
5879  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5880  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5881  * @cfg {String} sortable Specifies that the table should be sortable
5882  * @cfg {String} summary Specifies a summary of the content of a table
5883  * @cfg {Number} width Specifies the width of a table
5884  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5885  * 
5886  * @cfg {boolean} striped Should the rows be alternative striped
5887  * @cfg {boolean} bordered Add borders to the table
5888  * @cfg {boolean} hover Add hover highlighting
5889  * @cfg {boolean} condensed Format condensed
5890  * @cfg {boolean} responsive Format condensed
5891  * @cfg {Boolean} loadMask (true|false) default false
5892  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5893  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5894  * @cfg {Boolean} rowSelection (true|false) default false
5895  * @cfg {Boolean} cellSelection (true|false) default false
5896  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5897  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5898  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5899  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5900  
5901  * 
5902  * @constructor
5903  * Create a new Table
5904  * @param {Object} config The config object
5905  */
5906
5907 Roo.bootstrap.Table = function(config){
5908     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5909     
5910   
5911     
5912     // BC...
5913     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5914     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5915     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5916     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5917     
5918     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5919     if (this.sm) {
5920         this.sm.grid = this;
5921         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5922         this.sm = this.selModel;
5923         this.sm.xmodule = this.xmodule || false;
5924     }
5925     
5926     if (this.cm && typeof(this.cm.config) == 'undefined') {
5927         this.colModel = new Roo.grid.ColumnModel(this.cm);
5928         this.cm = this.colModel;
5929         this.cm.xmodule = this.xmodule || false;
5930     }
5931     if (this.store) {
5932         this.store= Roo.factory(this.store, Roo.data);
5933         this.ds = this.store;
5934         this.ds.xmodule = this.xmodule || false;
5935          
5936     }
5937     if (this.footer && this.store) {
5938         this.footer.dataSource = this.ds;
5939         this.footer = Roo.factory(this.footer);
5940     }
5941     
5942     /** @private */
5943     this.addEvents({
5944         /**
5945          * @event cellclick
5946          * Fires when a cell is clicked
5947          * @param {Roo.bootstrap.Table} this
5948          * @param {Roo.Element} el
5949          * @param {Number} rowIndex
5950          * @param {Number} columnIndex
5951          * @param {Roo.EventObject} e
5952          */
5953         "cellclick" : true,
5954         /**
5955          * @event celldblclick
5956          * Fires when a cell is double clicked
5957          * @param {Roo.bootstrap.Table} this
5958          * @param {Roo.Element} el
5959          * @param {Number} rowIndex
5960          * @param {Number} columnIndex
5961          * @param {Roo.EventObject} e
5962          */
5963         "celldblclick" : true,
5964         /**
5965          * @event rowclick
5966          * Fires when a row is clicked
5967          * @param {Roo.bootstrap.Table} this
5968          * @param {Roo.Element} el
5969          * @param {Number} rowIndex
5970          * @param {Roo.EventObject} e
5971          */
5972         "rowclick" : true,
5973         /**
5974          * @event rowdblclick
5975          * Fires when a row is double clicked
5976          * @param {Roo.bootstrap.Table} this
5977          * @param {Roo.Element} el
5978          * @param {Number} rowIndex
5979          * @param {Roo.EventObject} e
5980          */
5981         "rowdblclick" : true,
5982         /**
5983          * @event mouseover
5984          * Fires when a mouseover occur
5985          * @param {Roo.bootstrap.Table} this
5986          * @param {Roo.Element} el
5987          * @param {Number} rowIndex
5988          * @param {Number} columnIndex
5989          * @param {Roo.EventObject} e
5990          */
5991         "mouseover" : true,
5992         /**
5993          * @event mouseout
5994          * Fires when a mouseout occur
5995          * @param {Roo.bootstrap.Table} this
5996          * @param {Roo.Element} el
5997          * @param {Number} rowIndex
5998          * @param {Number} columnIndex
5999          * @param {Roo.EventObject} e
6000          */
6001         "mouseout" : true,
6002         /**
6003          * @event rowclass
6004          * Fires when a row is rendered, so you can change add a style to it.
6005          * @param {Roo.bootstrap.Table} this
6006          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6007          */
6008         'rowclass' : true,
6009           /**
6010          * @event rowsrendered
6011          * Fires when all the  rows have been rendered
6012          * @param {Roo.bootstrap.Table} this
6013          */
6014         'rowsrendered' : true,
6015         /**
6016          * @event contextmenu
6017          * The raw contextmenu event for the entire grid.
6018          * @param {Roo.EventObject} e
6019          */
6020         "contextmenu" : true,
6021         /**
6022          * @event rowcontextmenu
6023          * Fires when a row is right clicked
6024          * @param {Roo.bootstrap.Table} this
6025          * @param {Number} rowIndex
6026          * @param {Roo.EventObject} e
6027          */
6028         "rowcontextmenu" : true,
6029         /**
6030          * @event cellcontextmenu
6031          * Fires when a cell is right clicked
6032          * @param {Roo.bootstrap.Table} this
6033          * @param {Number} rowIndex
6034          * @param {Number} cellIndex
6035          * @param {Roo.EventObject} e
6036          */
6037          "cellcontextmenu" : true,
6038          /**
6039          * @event headercontextmenu
6040          * Fires when a header is right clicked
6041          * @param {Roo.bootstrap.Table} this
6042          * @param {Number} columnIndex
6043          * @param {Roo.EventObject} e
6044          */
6045         "headercontextmenu" : true
6046     });
6047 };
6048
6049 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6050     
6051     cls: false,
6052     align: false,
6053     bgcolor: false,
6054     border: false,
6055     cellpadding: false,
6056     cellspacing: false,
6057     frame: false,
6058     rules: false,
6059     sortable: false,
6060     summary: false,
6061     width: false,
6062     striped : false,
6063     scrollBody : false,
6064     bordered: false,
6065     hover:  false,
6066     condensed : false,
6067     responsive : false,
6068     sm : false,
6069     cm : false,
6070     store : false,
6071     loadMask : false,
6072     footerShow : true,
6073     headerShow : true,
6074   
6075     rowSelection : false,
6076     cellSelection : false,
6077     layout : false,
6078     
6079     // Roo.Element - the tbody
6080     mainBody: false,
6081     // Roo.Element - thead element
6082     mainHead: false,
6083     
6084     container: false, // used by gridpanel...
6085     
6086     lazyLoad : false,
6087     
6088     CSS : Roo.util.CSS,
6089     
6090     auto_hide_footer : false,
6091     
6092     getAutoCreate : function()
6093     {
6094         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6095         
6096         cfg = {
6097             tag: 'table',
6098             cls : 'table',
6099             cn : []
6100         };
6101         if (this.scrollBody) {
6102             cfg.cls += ' table-body-fixed';
6103         }    
6104         if (this.striped) {
6105             cfg.cls += ' table-striped';
6106         }
6107         
6108         if (this.hover) {
6109             cfg.cls += ' table-hover';
6110         }
6111         if (this.bordered) {
6112             cfg.cls += ' table-bordered';
6113         }
6114         if (this.condensed) {
6115             cfg.cls += ' table-condensed';
6116         }
6117         if (this.responsive) {
6118             cfg.cls += ' table-responsive';
6119         }
6120         
6121         if (this.cls) {
6122             cfg.cls+=  ' ' +this.cls;
6123         }
6124         
6125         // this lot should be simplifed...
6126         var _t = this;
6127         var cp = [
6128             'align',
6129             'bgcolor',
6130             'border',
6131             'cellpadding',
6132             'cellspacing',
6133             'frame',
6134             'rules',
6135             'sortable',
6136             'summary',
6137             'width'
6138         ].forEach(function(k) {
6139             if (_t[k]) {
6140                 cfg[k] = _t[k];
6141             }
6142         });
6143         
6144         
6145         if (this.layout) {
6146             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6147         }
6148         
6149         if(this.store || this.cm){
6150             if(this.headerShow){
6151                 cfg.cn.push(this.renderHeader());
6152             }
6153             
6154             cfg.cn.push(this.renderBody());
6155             
6156             if(this.footerShow){
6157                 cfg.cn.push(this.renderFooter());
6158             }
6159             // where does this come from?
6160             //cfg.cls+=  ' TableGrid';
6161         }
6162         
6163         return { cn : [ cfg ] };
6164     },
6165     
6166     initEvents : function()
6167     {   
6168         if(!this.store || !this.cm){
6169             return;
6170         }
6171         if (this.selModel) {
6172             this.selModel.initEvents();
6173         }
6174         
6175         
6176         //Roo.log('initEvents with ds!!!!');
6177         
6178         this.mainBody = this.el.select('tbody', true).first();
6179         this.mainHead = this.el.select('thead', true).first();
6180         this.mainFoot = this.el.select('tfoot', true).first();
6181         
6182         
6183         
6184         var _this = this;
6185         
6186         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6187             e.on('click', _this.sort, _this);
6188         });
6189         
6190         this.mainBody.on("click", this.onClick, this);
6191         this.mainBody.on("dblclick", this.onDblClick, this);
6192         
6193         // why is this done????? = it breaks dialogs??
6194         //this.parent().el.setStyle('position', 'relative');
6195         
6196         
6197         if (this.footer) {
6198             this.footer.parentId = this.id;
6199             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6200             
6201             if(this.lazyLoad){
6202                 this.el.select('tfoot tr td').first().addClass('hide');
6203             }
6204         } 
6205         
6206         if(this.loadMask) {
6207             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6208         }
6209         
6210         this.store.on('load', this.onLoad, this);
6211         this.store.on('beforeload', this.onBeforeLoad, this);
6212         this.store.on('update', this.onUpdate, this);
6213         this.store.on('add', this.onAdd, this);
6214         this.store.on("clear", this.clear, this);
6215         
6216         this.el.on("contextmenu", this.onContextMenu, this);
6217         
6218         this.mainBody.on('scroll', this.onBodyScroll, this);
6219         
6220         this.cm.on("headerchange", this.onHeaderChange, this);
6221         
6222         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6223         
6224     },
6225     
6226     onContextMenu : function(e, t)
6227     {
6228         this.processEvent("contextmenu", e);
6229     },
6230     
6231     processEvent : function(name, e)
6232     {
6233         if (name != 'touchstart' ) {
6234             this.fireEvent(name, e);    
6235         }
6236         
6237         var t = e.getTarget();
6238         
6239         var cell = Roo.get(t);
6240         
6241         if(!cell){
6242             return;
6243         }
6244         
6245         if(cell.findParent('tfoot', false, true)){
6246             return;
6247         }
6248         
6249         if(cell.findParent('thead', false, true)){
6250             
6251             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6252                 cell = Roo.get(t).findParent('th', false, true);
6253                 if (!cell) {
6254                     Roo.log("failed to find th in thead?");
6255                     Roo.log(e.getTarget());
6256                     return;
6257                 }
6258             }
6259             
6260             var cellIndex = cell.dom.cellIndex;
6261             
6262             var ename = name == 'touchstart' ? 'click' : name;
6263             this.fireEvent("header" + ename, this, cellIndex, e);
6264             
6265             return;
6266         }
6267         
6268         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6269             cell = Roo.get(t).findParent('td', false, true);
6270             if (!cell) {
6271                 Roo.log("failed to find th in tbody?");
6272                 Roo.log(e.getTarget());
6273                 return;
6274             }
6275         }
6276         
6277         var row = cell.findParent('tr', false, true);
6278         var cellIndex = cell.dom.cellIndex;
6279         var rowIndex = row.dom.rowIndex - 1;
6280         
6281         if(row !== false){
6282             
6283             this.fireEvent("row" + name, this, rowIndex, e);
6284             
6285             if(cell !== false){
6286             
6287                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6288             }
6289         }
6290         
6291     },
6292     
6293     onMouseover : function(e, el)
6294     {
6295         var cell = Roo.get(el);
6296         
6297         if(!cell){
6298             return;
6299         }
6300         
6301         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6302             cell = cell.findParent('td', false, true);
6303         }
6304         
6305         var row = cell.findParent('tr', false, true);
6306         var cellIndex = cell.dom.cellIndex;
6307         var rowIndex = row.dom.rowIndex - 1; // start from 0
6308         
6309         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6310         
6311     },
6312     
6313     onMouseout : function(e, el)
6314     {
6315         var cell = Roo.get(el);
6316         
6317         if(!cell){
6318             return;
6319         }
6320         
6321         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6322             cell = cell.findParent('td', false, true);
6323         }
6324         
6325         var row = cell.findParent('tr', false, true);
6326         var cellIndex = cell.dom.cellIndex;
6327         var rowIndex = row.dom.rowIndex - 1; // start from 0
6328         
6329         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6330         
6331     },
6332     
6333     onClick : function(e, el)
6334     {
6335         var cell = Roo.get(el);
6336         
6337         if(!cell || (!this.cellSelection && !this.rowSelection)){
6338             return;
6339         }
6340         
6341         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6342             cell = cell.findParent('td', false, true);
6343         }
6344         
6345         if(!cell || typeof(cell) == 'undefined'){
6346             return;
6347         }
6348         
6349         var row = cell.findParent('tr', false, true);
6350         
6351         if(!row || typeof(row) == 'undefined'){
6352             return;
6353         }
6354         
6355         var cellIndex = cell.dom.cellIndex;
6356         var rowIndex = this.getRowIndex(row);
6357         
6358         // why??? - should these not be based on SelectionModel?
6359         if(this.cellSelection){
6360             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6361         }
6362         
6363         if(this.rowSelection){
6364             this.fireEvent('rowclick', this, row, rowIndex, e);
6365         }
6366         
6367         
6368     },
6369         
6370     onDblClick : function(e,el)
6371     {
6372         var cell = Roo.get(el);
6373         
6374         if(!cell || (!this.cellSelection && !this.rowSelection)){
6375             return;
6376         }
6377         
6378         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6379             cell = cell.findParent('td', false, true);
6380         }
6381         
6382         if(!cell || typeof(cell) == 'undefined'){
6383             return;
6384         }
6385         
6386         var row = cell.findParent('tr', false, true);
6387         
6388         if(!row || typeof(row) == 'undefined'){
6389             return;
6390         }
6391         
6392         var cellIndex = cell.dom.cellIndex;
6393         var rowIndex = this.getRowIndex(row);
6394         
6395         if(this.cellSelection){
6396             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6397         }
6398         
6399         if(this.rowSelection){
6400             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6401         }
6402     },
6403     
6404     sort : function(e,el)
6405     {
6406         var col = Roo.get(el);
6407         
6408         if(!col.hasClass('sortable')){
6409             return;
6410         }
6411         
6412         var sort = col.attr('sort');
6413         var dir = 'ASC';
6414         
6415         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6416             dir = 'DESC';
6417         }
6418         
6419         this.store.sortInfo = {field : sort, direction : dir};
6420         
6421         if (this.footer) {
6422             Roo.log("calling footer first");
6423             this.footer.onClick('first');
6424         } else {
6425         
6426             this.store.load({ params : { start : 0 } });
6427         }
6428     },
6429     
6430     renderHeader : function()
6431     {
6432         var header = {
6433             tag: 'thead',
6434             cn : []
6435         };
6436         
6437         var cm = this.cm;
6438         this.totalWidth = 0;
6439         
6440         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6441             
6442             var config = cm.config[i];
6443             
6444             var c = {
6445                 tag: 'th',
6446                 cls : 'x-hcol-' + i,
6447                 style : '',
6448                 html: cm.getColumnHeader(i)
6449             };
6450             
6451             var hh = '';
6452             
6453             if(typeof(config.sortable) != 'undefined' && config.sortable){
6454                 c.cls = 'sortable';
6455                 c.html = '<i class="glyphicon"></i>' + c.html;
6456             }
6457             
6458             if(typeof(config.lgHeader) != 'undefined'){
6459                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6460             }
6461             
6462             if(typeof(config.mdHeader) != 'undefined'){
6463                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6464             }
6465             
6466             if(typeof(config.smHeader) != 'undefined'){
6467                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6468             }
6469             
6470             if(typeof(config.xsHeader) != 'undefined'){
6471                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6472             }
6473             
6474             if(hh.length){
6475                 c.html = hh;
6476             }
6477             
6478             if(typeof(config.tooltip) != 'undefined'){
6479                 c.tooltip = config.tooltip;
6480             }
6481             
6482             if(typeof(config.colspan) != 'undefined'){
6483                 c.colspan = config.colspan;
6484             }
6485             
6486             if(typeof(config.hidden) != 'undefined' && config.hidden){
6487                 c.style += ' display:none;';
6488             }
6489             
6490             if(typeof(config.dataIndex) != 'undefined'){
6491                 c.sort = config.dataIndex;
6492             }
6493             
6494            
6495             
6496             if(typeof(config.align) != 'undefined' && config.align.length){
6497                 c.style += ' text-align:' + config.align + ';';
6498             }
6499             
6500             if(typeof(config.width) != 'undefined'){
6501                 c.style += ' width:' + config.width + 'px;';
6502                 this.totalWidth += config.width;
6503             } else {
6504                 this.totalWidth += 100; // assume minimum of 100 per column?
6505             }
6506             
6507             if(typeof(config.cls) != 'undefined'){
6508                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6509             }
6510             
6511             ['xs','sm','md','lg'].map(function(size){
6512                 
6513                 if(typeof(config[size]) == 'undefined'){
6514                     return;
6515                 }
6516                 
6517                 if (!config[size]) { // 0 = hidden
6518                     c.cls += ' hidden-' + size;
6519                     return;
6520                 }
6521                 
6522                 c.cls += ' col-' + size + '-' + config[size];
6523
6524             });
6525             
6526             header.cn.push(c)
6527         }
6528         
6529         return header;
6530     },
6531     
6532     renderBody : function()
6533     {
6534         var body = {
6535             tag: 'tbody',
6536             cn : [
6537                 {
6538                     tag: 'tr',
6539                     cn : [
6540                         {
6541                             tag : 'td',
6542                             colspan :  this.cm.getColumnCount()
6543                         }
6544                     ]
6545                 }
6546             ]
6547         };
6548         
6549         return body;
6550     },
6551     
6552     renderFooter : function()
6553     {
6554         var footer = {
6555             tag: 'tfoot',
6556             cn : [
6557                 {
6558                     tag: 'tr',
6559                     cn : [
6560                         {
6561                             tag : 'td',
6562                             colspan :  this.cm.getColumnCount()
6563                         }
6564                     ]
6565                 }
6566             ]
6567         };
6568         
6569         return footer;
6570     },
6571     
6572     
6573     
6574     onLoad : function()
6575     {
6576 //        Roo.log('ds onload');
6577         this.clear();
6578         
6579         var _this = this;
6580         var cm = this.cm;
6581         var ds = this.store;
6582         
6583         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6584             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6585             if (_this.store.sortInfo) {
6586                     
6587                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6588                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6589                 }
6590                 
6591                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6592                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6593                 }
6594             }
6595         });
6596         
6597         var tbody =  this.mainBody;
6598               
6599         if(ds.getCount() > 0){
6600             ds.data.each(function(d,rowIndex){
6601                 var row =  this.renderRow(cm, ds, rowIndex);
6602                 
6603                 tbody.createChild(row);
6604                 
6605                 var _this = this;
6606                 
6607                 if(row.cellObjects.length){
6608                     Roo.each(row.cellObjects, function(r){
6609                         _this.renderCellObject(r);
6610                     })
6611                 }
6612                 
6613             }, this);
6614         }
6615         
6616         var tfoot = this.el.select('tfoot', true).first();
6617         
6618         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6619             
6620             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6621             
6622             var total = this.ds.getTotalCount();
6623             
6624             if(this.footer.pageSize < total){
6625                 this.mainFoot.show();
6626             }
6627         }
6628         
6629         Roo.each(this.el.select('tbody td', true).elements, function(e){
6630             e.on('mouseover', _this.onMouseover, _this);
6631         });
6632         
6633         Roo.each(this.el.select('tbody td', true).elements, function(e){
6634             e.on('mouseout', _this.onMouseout, _this);
6635         });
6636         this.fireEvent('rowsrendered', this);
6637         
6638         this.autoSize();
6639     },
6640     
6641     
6642     onUpdate : function(ds,record)
6643     {
6644         this.refreshRow(record);
6645         this.autoSize();
6646     },
6647     
6648     onRemove : function(ds, record, index, isUpdate){
6649         if(isUpdate !== true){
6650             this.fireEvent("beforerowremoved", this, index, record);
6651         }
6652         var bt = this.mainBody.dom;
6653         
6654         var rows = this.el.select('tbody > tr', true).elements;
6655         
6656         if(typeof(rows[index]) != 'undefined'){
6657             bt.removeChild(rows[index].dom);
6658         }
6659         
6660 //        if(bt.rows[index]){
6661 //            bt.removeChild(bt.rows[index]);
6662 //        }
6663         
6664         if(isUpdate !== true){
6665             //this.stripeRows(index);
6666             //this.syncRowHeights(index, index);
6667             //this.layout();
6668             this.fireEvent("rowremoved", this, index, record);
6669         }
6670     },
6671     
6672     onAdd : function(ds, records, rowIndex)
6673     {
6674         //Roo.log('on Add called');
6675         // - note this does not handle multiple adding very well..
6676         var bt = this.mainBody.dom;
6677         for (var i =0 ; i < records.length;i++) {
6678             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6679             //Roo.log(records[i]);
6680             //Roo.log(this.store.getAt(rowIndex+i));
6681             this.insertRow(this.store, rowIndex + i, false);
6682             return;
6683         }
6684         
6685     },
6686     
6687     
6688     refreshRow : function(record){
6689         var ds = this.store, index;
6690         if(typeof record == 'number'){
6691             index = record;
6692             record = ds.getAt(index);
6693         }else{
6694             index = ds.indexOf(record);
6695         }
6696         this.insertRow(ds, index, true);
6697         this.autoSize();
6698         this.onRemove(ds, record, index+1, true);
6699         this.autoSize();
6700         //this.syncRowHeights(index, index);
6701         //this.layout();
6702         this.fireEvent("rowupdated", this, index, record);
6703     },
6704     
6705     insertRow : function(dm, rowIndex, isUpdate){
6706         
6707         if(!isUpdate){
6708             this.fireEvent("beforerowsinserted", this, rowIndex);
6709         }
6710             //var s = this.getScrollState();
6711         var row = this.renderRow(this.cm, this.store, rowIndex);
6712         // insert before rowIndex..
6713         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6714         
6715         var _this = this;
6716                 
6717         if(row.cellObjects.length){
6718             Roo.each(row.cellObjects, function(r){
6719                 _this.renderCellObject(r);
6720             })
6721         }
6722             
6723         if(!isUpdate){
6724             this.fireEvent("rowsinserted", this, rowIndex);
6725             //this.syncRowHeights(firstRow, lastRow);
6726             //this.stripeRows(firstRow);
6727             //this.layout();
6728         }
6729         
6730     },
6731     
6732     
6733     getRowDom : function(rowIndex)
6734     {
6735         var rows = this.el.select('tbody > tr', true).elements;
6736         
6737         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6738         
6739     },
6740     // returns the object tree for a tr..
6741   
6742     
6743     renderRow : function(cm, ds, rowIndex) 
6744     {
6745         var d = ds.getAt(rowIndex);
6746         
6747         var row = {
6748             tag : 'tr',
6749             cls : 'x-row-' + rowIndex,
6750             cn : []
6751         };
6752             
6753         var cellObjects = [];
6754         
6755         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6756             var config = cm.config[i];
6757             
6758             var renderer = cm.getRenderer(i);
6759             var value = '';
6760             var id = false;
6761             
6762             if(typeof(renderer) !== 'undefined'){
6763                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6764             }
6765             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6766             // and are rendered into the cells after the row is rendered - using the id for the element.
6767             
6768             if(typeof(value) === 'object'){
6769                 id = Roo.id();
6770                 cellObjects.push({
6771                     container : id,
6772                     cfg : value 
6773                 })
6774             }
6775             
6776             var rowcfg = {
6777                 record: d,
6778                 rowIndex : rowIndex,
6779                 colIndex : i,
6780                 rowClass : ''
6781             };
6782
6783             this.fireEvent('rowclass', this, rowcfg);
6784             
6785             var td = {
6786                 tag: 'td',
6787                 cls : rowcfg.rowClass + ' x-col-' + i,
6788                 style: '',
6789                 html: (typeof(value) === 'object') ? '' : value
6790             };
6791             
6792             if (id) {
6793                 td.id = id;
6794             }
6795             
6796             if(typeof(config.colspan) != 'undefined'){
6797                 td.colspan = config.colspan;
6798             }
6799             
6800             if(typeof(config.hidden) != 'undefined' && config.hidden){
6801                 td.style += ' display:none;';
6802             }
6803             
6804             if(typeof(config.align) != 'undefined' && config.align.length){
6805                 td.style += ' text-align:' + config.align + ';';
6806             }
6807             if(typeof(config.valign) != 'undefined' && config.valign.length){
6808                 td.style += ' vertical-align:' + config.valign + ';';
6809             }
6810             
6811             if(typeof(config.width) != 'undefined'){
6812                 td.style += ' width:' +  config.width + 'px;';
6813             }
6814             
6815             if(typeof(config.cursor) != 'undefined'){
6816                 td.style += ' cursor:' +  config.cursor + ';';
6817             }
6818             
6819             if(typeof(config.cls) != 'undefined'){
6820                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6821             }
6822             
6823             ['xs','sm','md','lg'].map(function(size){
6824                 
6825                 if(typeof(config[size]) == 'undefined'){
6826                     return;
6827                 }
6828                 
6829                 if (!config[size]) { // 0 = hidden
6830                     td.cls += ' hidden-' + size;
6831                     return;
6832                 }
6833                 
6834                 td.cls += ' col-' + size + '-' + config[size];
6835
6836             });
6837             
6838             row.cn.push(td);
6839            
6840         }
6841         
6842         row.cellObjects = cellObjects;
6843         
6844         return row;
6845           
6846     },
6847     
6848     
6849     
6850     onBeforeLoad : function()
6851     {
6852         
6853     },
6854      /**
6855      * Remove all rows
6856      */
6857     clear : function()
6858     {
6859         this.el.select('tbody', true).first().dom.innerHTML = '';
6860     },
6861     /**
6862      * Show or hide a row.
6863      * @param {Number} rowIndex to show or hide
6864      * @param {Boolean} state hide
6865      */
6866     setRowVisibility : function(rowIndex, state)
6867     {
6868         var bt = this.mainBody.dom;
6869         
6870         var rows = this.el.select('tbody > tr', true).elements;
6871         
6872         if(typeof(rows[rowIndex]) == 'undefined'){
6873             return;
6874         }
6875         rows[rowIndex].dom.style.display = state ? '' : 'none';
6876     },
6877     
6878     
6879     getSelectionModel : function(){
6880         if(!this.selModel){
6881             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6882         }
6883         return this.selModel;
6884     },
6885     /*
6886      * Render the Roo.bootstrap object from renderder
6887      */
6888     renderCellObject : function(r)
6889     {
6890         var _this = this;
6891         
6892         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6893         
6894         var t = r.cfg.render(r.container);
6895         
6896         if(r.cfg.cn){
6897             Roo.each(r.cfg.cn, function(c){
6898                 var child = {
6899                     container: t.getChildContainer(),
6900                     cfg: c
6901                 };
6902                 _this.renderCellObject(child);
6903             })
6904         }
6905     },
6906     
6907     getRowIndex : function(row)
6908     {
6909         var rowIndex = -1;
6910         
6911         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6912             if(el != row){
6913                 return;
6914             }
6915             
6916             rowIndex = index;
6917         });
6918         
6919         return rowIndex;
6920     },
6921      /**
6922      * Returns the grid's underlying element = used by panel.Grid
6923      * @return {Element} The element
6924      */
6925     getGridEl : function(){
6926         return this.el;
6927     },
6928      /**
6929      * Forces a resize - used by panel.Grid
6930      * @return {Element} The element
6931      */
6932     autoSize : function()
6933     {
6934         //var ctr = Roo.get(this.container.dom.parentElement);
6935         var ctr = Roo.get(this.el.dom);
6936         
6937         var thd = this.getGridEl().select('thead',true).first();
6938         var tbd = this.getGridEl().select('tbody', true).first();
6939         var tfd = this.getGridEl().select('tfoot', true).first();
6940         
6941         var cw = ctr.getWidth();
6942         
6943         if (tbd) {
6944             
6945             tbd.setSize(ctr.getWidth(),
6946                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6947             );
6948             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6949             cw -= barsize;
6950         }
6951         cw = Math.max(cw, this.totalWidth);
6952         this.getGridEl().select('tr',true).setWidth(cw);
6953         // resize 'expandable coloumn?
6954         
6955         return; // we doe not have a view in this design..
6956         
6957     },
6958     onBodyScroll: function()
6959     {
6960         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6961         if(this.mainHead){
6962             this.mainHead.setStyle({
6963                 'position' : 'relative',
6964                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6965             });
6966         }
6967         
6968         if(this.lazyLoad){
6969             
6970             var scrollHeight = this.mainBody.dom.scrollHeight;
6971             
6972             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6973             
6974             var height = this.mainBody.getHeight();
6975             
6976             if(scrollHeight - height == scrollTop) {
6977                 
6978                 var total = this.ds.getTotalCount();
6979                 
6980                 if(this.footer.cursor + this.footer.pageSize < total){
6981                     
6982                     this.footer.ds.load({
6983                         params : {
6984                             start : this.footer.cursor + this.footer.pageSize,
6985                             limit : this.footer.pageSize
6986                         },
6987                         add : true
6988                     });
6989                 }
6990             }
6991             
6992         }
6993     },
6994     
6995     onHeaderChange : function()
6996     {
6997         var header = this.renderHeader();
6998         var table = this.el.select('table', true).first();
6999         
7000         this.mainHead.remove();
7001         this.mainHead = table.createChild(header, this.mainBody, false);
7002     },
7003     
7004     onHiddenChange : function(colModel, colIndex, hidden)
7005     {
7006         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7007         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7008         
7009         this.CSS.updateRule(thSelector, "display", "");
7010         this.CSS.updateRule(tdSelector, "display", "");
7011         
7012         if(hidden){
7013             this.CSS.updateRule(thSelector, "display", "none");
7014             this.CSS.updateRule(tdSelector, "display", "none");
7015         }
7016         
7017         this.onHeaderChange();
7018         this.onLoad();
7019         
7020     }
7021     
7022 });
7023
7024  
7025
7026  /*
7027  * - LGPL
7028  *
7029  * table cell
7030  * 
7031  */
7032
7033 /**
7034  * @class Roo.bootstrap.TableCell
7035  * @extends Roo.bootstrap.Component
7036  * Bootstrap TableCell class
7037  * @cfg {String} html cell contain text
7038  * @cfg {String} cls cell class
7039  * @cfg {String} tag cell tag (td|th) default td
7040  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7041  * @cfg {String} align Aligns the content in a cell
7042  * @cfg {String} axis Categorizes cells
7043  * @cfg {String} bgcolor Specifies the background color of a cell
7044  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7045  * @cfg {Number} colspan Specifies the number of columns a cell should span
7046  * @cfg {String} headers Specifies one or more header cells a cell is related to
7047  * @cfg {Number} height Sets the height of a cell
7048  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7049  * @cfg {Number} rowspan Sets the number of rows a cell should span
7050  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7051  * @cfg {String} valign Vertical aligns the content in a cell
7052  * @cfg {Number} width Specifies the width of a cell
7053  * 
7054  * @constructor
7055  * Create a new TableCell
7056  * @param {Object} config The config object
7057  */
7058
7059 Roo.bootstrap.TableCell = function(config){
7060     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7061 };
7062
7063 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7064     
7065     html: false,
7066     cls: false,
7067     tag: false,
7068     abbr: false,
7069     align: false,
7070     axis: false,
7071     bgcolor: false,
7072     charoff: false,
7073     colspan: false,
7074     headers: false,
7075     height: false,
7076     nowrap: false,
7077     rowspan: false,
7078     scope: false,
7079     valign: false,
7080     width: false,
7081     
7082     
7083     getAutoCreate : function(){
7084         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7085         
7086         cfg = {
7087             tag: 'td'
7088         };
7089         
7090         if(this.tag){
7091             cfg.tag = this.tag;
7092         }
7093         
7094         if (this.html) {
7095             cfg.html=this.html
7096         }
7097         if (this.cls) {
7098             cfg.cls=this.cls
7099         }
7100         if (this.abbr) {
7101             cfg.abbr=this.abbr
7102         }
7103         if (this.align) {
7104             cfg.align=this.align
7105         }
7106         if (this.axis) {
7107             cfg.axis=this.axis
7108         }
7109         if (this.bgcolor) {
7110             cfg.bgcolor=this.bgcolor
7111         }
7112         if (this.charoff) {
7113             cfg.charoff=this.charoff
7114         }
7115         if (this.colspan) {
7116             cfg.colspan=this.colspan
7117         }
7118         if (this.headers) {
7119             cfg.headers=this.headers
7120         }
7121         if (this.height) {
7122             cfg.height=this.height
7123         }
7124         if (this.nowrap) {
7125             cfg.nowrap=this.nowrap
7126         }
7127         if (this.rowspan) {
7128             cfg.rowspan=this.rowspan
7129         }
7130         if (this.scope) {
7131             cfg.scope=this.scope
7132         }
7133         if (this.valign) {
7134             cfg.valign=this.valign
7135         }
7136         if (this.width) {
7137             cfg.width=this.width
7138         }
7139         
7140         
7141         return cfg;
7142     }
7143    
7144 });
7145
7146  
7147
7148  /*
7149  * - LGPL
7150  *
7151  * table row
7152  * 
7153  */
7154
7155 /**
7156  * @class Roo.bootstrap.TableRow
7157  * @extends Roo.bootstrap.Component
7158  * Bootstrap TableRow class
7159  * @cfg {String} cls row class
7160  * @cfg {String} align Aligns the content in a table row
7161  * @cfg {String} bgcolor Specifies a background color for a table row
7162  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7163  * @cfg {String} valign Vertical aligns the content in a table row
7164  * 
7165  * @constructor
7166  * Create a new TableRow
7167  * @param {Object} config The config object
7168  */
7169
7170 Roo.bootstrap.TableRow = function(config){
7171     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7172 };
7173
7174 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7175     
7176     cls: false,
7177     align: false,
7178     bgcolor: false,
7179     charoff: false,
7180     valign: false,
7181     
7182     getAutoCreate : function(){
7183         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7184         
7185         cfg = {
7186             tag: 'tr'
7187         };
7188             
7189         if(this.cls){
7190             cfg.cls = this.cls;
7191         }
7192         if(this.align){
7193             cfg.align = this.align;
7194         }
7195         if(this.bgcolor){
7196             cfg.bgcolor = this.bgcolor;
7197         }
7198         if(this.charoff){
7199             cfg.charoff = this.charoff;
7200         }
7201         if(this.valign){
7202             cfg.valign = this.valign;
7203         }
7204         
7205         return cfg;
7206     }
7207    
7208 });
7209
7210  
7211
7212  /*
7213  * - LGPL
7214  *
7215  * table body
7216  * 
7217  */
7218
7219 /**
7220  * @class Roo.bootstrap.TableBody
7221  * @extends Roo.bootstrap.Component
7222  * Bootstrap TableBody class
7223  * @cfg {String} cls element class
7224  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7225  * @cfg {String} align Aligns the content inside the element
7226  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7227  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7228  * 
7229  * @constructor
7230  * Create a new TableBody
7231  * @param {Object} config The config object
7232  */
7233
7234 Roo.bootstrap.TableBody = function(config){
7235     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7236 };
7237
7238 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7239     
7240     cls: false,
7241     tag: false,
7242     align: false,
7243     charoff: false,
7244     valign: false,
7245     
7246     getAutoCreate : function(){
7247         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7248         
7249         cfg = {
7250             tag: 'tbody'
7251         };
7252             
7253         if (this.cls) {
7254             cfg.cls=this.cls
7255         }
7256         if(this.tag){
7257             cfg.tag = this.tag;
7258         }
7259         
7260         if(this.align){
7261             cfg.align = this.align;
7262         }
7263         if(this.charoff){
7264             cfg.charoff = this.charoff;
7265         }
7266         if(this.valign){
7267             cfg.valign = this.valign;
7268         }
7269         
7270         return cfg;
7271     }
7272     
7273     
7274 //    initEvents : function()
7275 //    {
7276 //        
7277 //        if(!this.store){
7278 //            return;
7279 //        }
7280 //        
7281 //        this.store = Roo.factory(this.store, Roo.data);
7282 //        this.store.on('load', this.onLoad, this);
7283 //        
7284 //        this.store.load();
7285 //        
7286 //    },
7287 //    
7288 //    onLoad: function () 
7289 //    {   
7290 //        this.fireEvent('load', this);
7291 //    }
7292 //    
7293 //   
7294 });
7295
7296  
7297
7298  /*
7299  * Based on:
7300  * Ext JS Library 1.1.1
7301  * Copyright(c) 2006-2007, Ext JS, LLC.
7302  *
7303  * Originally Released Under LGPL - original licence link has changed is not relivant.
7304  *
7305  * Fork - LGPL
7306  * <script type="text/javascript">
7307  */
7308
7309 // as we use this in bootstrap.
7310 Roo.namespace('Roo.form');
7311  /**
7312  * @class Roo.form.Action
7313  * Internal Class used to handle form actions
7314  * @constructor
7315  * @param {Roo.form.BasicForm} el The form element or its id
7316  * @param {Object} config Configuration options
7317  */
7318
7319  
7320  
7321 // define the action interface
7322 Roo.form.Action = function(form, options){
7323     this.form = form;
7324     this.options = options || {};
7325 };
7326 /**
7327  * Client Validation Failed
7328  * @const 
7329  */
7330 Roo.form.Action.CLIENT_INVALID = 'client';
7331 /**
7332  * Server Validation Failed
7333  * @const 
7334  */
7335 Roo.form.Action.SERVER_INVALID = 'server';
7336  /**
7337  * Connect to Server Failed
7338  * @const 
7339  */
7340 Roo.form.Action.CONNECT_FAILURE = 'connect';
7341 /**
7342  * Reading Data from Server Failed
7343  * @const 
7344  */
7345 Roo.form.Action.LOAD_FAILURE = 'load';
7346
7347 Roo.form.Action.prototype = {
7348     type : 'default',
7349     failureType : undefined,
7350     response : undefined,
7351     result : undefined,
7352
7353     // interface method
7354     run : function(options){
7355
7356     },
7357
7358     // interface method
7359     success : function(response){
7360
7361     },
7362
7363     // interface method
7364     handleResponse : function(response){
7365
7366     },
7367
7368     // default connection failure
7369     failure : function(response){
7370         
7371         this.response = response;
7372         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7373         this.form.afterAction(this, false);
7374     },
7375
7376     processResponse : function(response){
7377         this.response = response;
7378         if(!response.responseText){
7379             return true;
7380         }
7381         this.result = this.handleResponse(response);
7382         return this.result;
7383     },
7384
7385     // utility functions used internally
7386     getUrl : function(appendParams){
7387         var url = this.options.url || this.form.url || this.form.el.dom.action;
7388         if(appendParams){
7389             var p = this.getParams();
7390             if(p){
7391                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7392             }
7393         }
7394         return url;
7395     },
7396
7397     getMethod : function(){
7398         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7399     },
7400
7401     getParams : function(){
7402         var bp = this.form.baseParams;
7403         var p = this.options.params;
7404         if(p){
7405             if(typeof p == "object"){
7406                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7407             }else if(typeof p == 'string' && bp){
7408                 p += '&' + Roo.urlEncode(bp);
7409             }
7410         }else if(bp){
7411             p = Roo.urlEncode(bp);
7412         }
7413         return p;
7414     },
7415
7416     createCallback : function(){
7417         return {
7418             success: this.success,
7419             failure: this.failure,
7420             scope: this,
7421             timeout: (this.form.timeout*1000),
7422             upload: this.form.fileUpload ? this.success : undefined
7423         };
7424     }
7425 };
7426
7427 Roo.form.Action.Submit = function(form, options){
7428     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7429 };
7430
7431 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7432     type : 'submit',
7433
7434     haveProgress : false,
7435     uploadComplete : false,
7436     
7437     // uploadProgress indicator.
7438     uploadProgress : function()
7439     {
7440         if (!this.form.progressUrl) {
7441             return;
7442         }
7443         
7444         if (!this.haveProgress) {
7445             Roo.MessageBox.progress("Uploading", "Uploading");
7446         }
7447         if (this.uploadComplete) {
7448            Roo.MessageBox.hide();
7449            return;
7450         }
7451         
7452         this.haveProgress = true;
7453    
7454         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7455         
7456         var c = new Roo.data.Connection();
7457         c.request({
7458             url : this.form.progressUrl,
7459             params: {
7460                 id : uid
7461             },
7462             method: 'GET',
7463             success : function(req){
7464                //console.log(data);
7465                 var rdata = false;
7466                 var edata;
7467                 try  {
7468                    rdata = Roo.decode(req.responseText)
7469                 } catch (e) {
7470                     Roo.log("Invalid data from server..");
7471                     Roo.log(edata);
7472                     return;
7473                 }
7474                 if (!rdata || !rdata.success) {
7475                     Roo.log(rdata);
7476                     Roo.MessageBox.alert(Roo.encode(rdata));
7477                     return;
7478                 }
7479                 var data = rdata.data;
7480                 
7481                 if (this.uploadComplete) {
7482                    Roo.MessageBox.hide();
7483                    return;
7484                 }
7485                    
7486                 if (data){
7487                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7488                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7489                     );
7490                 }
7491                 this.uploadProgress.defer(2000,this);
7492             },
7493        
7494             failure: function(data) {
7495                 Roo.log('progress url failed ');
7496                 Roo.log(data);
7497             },
7498             scope : this
7499         });
7500            
7501     },
7502     
7503     
7504     run : function()
7505     {
7506         // run get Values on the form, so it syncs any secondary forms.
7507         this.form.getValues();
7508         
7509         var o = this.options;
7510         var method = this.getMethod();
7511         var isPost = method == 'POST';
7512         if(o.clientValidation === false || this.form.isValid()){
7513             
7514             if (this.form.progressUrl) {
7515                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7516                     (new Date() * 1) + '' + Math.random());
7517                     
7518             } 
7519             
7520             
7521             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7522                 form:this.form.el.dom,
7523                 url:this.getUrl(!isPost),
7524                 method: method,
7525                 params:isPost ? this.getParams() : null,
7526                 isUpload: this.form.fileUpload
7527             }));
7528             
7529             this.uploadProgress();
7530
7531         }else if (o.clientValidation !== false){ // client validation failed
7532             this.failureType = Roo.form.Action.CLIENT_INVALID;
7533             this.form.afterAction(this, false);
7534         }
7535     },
7536
7537     success : function(response)
7538     {
7539         this.uploadComplete= true;
7540         if (this.haveProgress) {
7541             Roo.MessageBox.hide();
7542         }
7543         
7544         
7545         var result = this.processResponse(response);
7546         if(result === true || result.success){
7547             this.form.afterAction(this, true);
7548             return;
7549         }
7550         if(result.errors){
7551             this.form.markInvalid(result.errors);
7552             this.failureType = Roo.form.Action.SERVER_INVALID;
7553         }
7554         this.form.afterAction(this, false);
7555     },
7556     failure : function(response)
7557     {
7558         this.uploadComplete= true;
7559         if (this.haveProgress) {
7560             Roo.MessageBox.hide();
7561         }
7562         
7563         this.response = response;
7564         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7565         this.form.afterAction(this, false);
7566     },
7567     
7568     handleResponse : function(response){
7569         if(this.form.errorReader){
7570             var rs = this.form.errorReader.read(response);
7571             var errors = [];
7572             if(rs.records){
7573                 for(var i = 0, len = rs.records.length; i < len; i++) {
7574                     var r = rs.records[i];
7575                     errors[i] = r.data;
7576                 }
7577             }
7578             if(errors.length < 1){
7579                 errors = null;
7580             }
7581             return {
7582                 success : rs.success,
7583                 errors : errors
7584             };
7585         }
7586         var ret = false;
7587         try {
7588             ret = Roo.decode(response.responseText);
7589         } catch (e) {
7590             ret = {
7591                 success: false,
7592                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7593                 errors : []
7594             };
7595         }
7596         return ret;
7597         
7598     }
7599 });
7600
7601
7602 Roo.form.Action.Load = function(form, options){
7603     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7604     this.reader = this.form.reader;
7605 };
7606
7607 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7608     type : 'load',
7609
7610     run : function(){
7611         
7612         Roo.Ajax.request(Roo.apply(
7613                 this.createCallback(), {
7614                     method:this.getMethod(),
7615                     url:this.getUrl(false),
7616                     params:this.getParams()
7617         }));
7618     },
7619
7620     success : function(response){
7621         
7622         var result = this.processResponse(response);
7623         if(result === true || !result.success || !result.data){
7624             this.failureType = Roo.form.Action.LOAD_FAILURE;
7625             this.form.afterAction(this, false);
7626             return;
7627         }
7628         this.form.clearInvalid();
7629         this.form.setValues(result.data);
7630         this.form.afterAction(this, true);
7631     },
7632
7633     handleResponse : function(response){
7634         if(this.form.reader){
7635             var rs = this.form.reader.read(response);
7636             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7637             return {
7638                 success : rs.success,
7639                 data : data
7640             };
7641         }
7642         return Roo.decode(response.responseText);
7643     }
7644 });
7645
7646 Roo.form.Action.ACTION_TYPES = {
7647     'load' : Roo.form.Action.Load,
7648     'submit' : Roo.form.Action.Submit
7649 };/*
7650  * - LGPL
7651  *
7652  * form
7653  *
7654  */
7655
7656 /**
7657  * @class Roo.bootstrap.Form
7658  * @extends Roo.bootstrap.Component
7659  * Bootstrap Form class
7660  * @cfg {String} method  GET | POST (default POST)
7661  * @cfg {String} labelAlign top | left (default top)
7662  * @cfg {String} align left  | right - for navbars
7663  * @cfg {Boolean} loadMask load mask when submit (default true)
7664
7665  *
7666  * @constructor
7667  * Create a new Form
7668  * @param {Object} config The config object
7669  */
7670
7671
7672 Roo.bootstrap.Form = function(config){
7673     
7674     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7675     
7676     Roo.bootstrap.Form.popover.apply();
7677     
7678     this.addEvents({
7679         /**
7680          * @event clientvalidation
7681          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7682          * @param {Form} this
7683          * @param {Boolean} valid true if the form has passed client-side validation
7684          */
7685         clientvalidation: true,
7686         /**
7687          * @event beforeaction
7688          * Fires before any action is performed. Return false to cancel the action.
7689          * @param {Form} this
7690          * @param {Action} action The action to be performed
7691          */
7692         beforeaction: true,
7693         /**
7694          * @event actionfailed
7695          * Fires when an action fails.
7696          * @param {Form} this
7697          * @param {Action} action The action that failed
7698          */
7699         actionfailed : true,
7700         /**
7701          * @event actioncomplete
7702          * Fires when an action is completed.
7703          * @param {Form} this
7704          * @param {Action} action The action that completed
7705          */
7706         actioncomplete : true
7707     });
7708 };
7709
7710 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7711
7712      /**
7713      * @cfg {String} method
7714      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7715      */
7716     method : 'POST',
7717     /**
7718      * @cfg {String} url
7719      * The URL to use for form actions if one isn't supplied in the action options.
7720      */
7721     /**
7722      * @cfg {Boolean} fileUpload
7723      * Set to true if this form is a file upload.
7724      */
7725
7726     /**
7727      * @cfg {Object} baseParams
7728      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7729      */
7730
7731     /**
7732      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7733      */
7734     timeout: 30,
7735     /**
7736      * @cfg {Sting} align (left|right) for navbar forms
7737      */
7738     align : 'left',
7739
7740     // private
7741     activeAction : null,
7742
7743     /**
7744      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7745      * element by passing it or its id or mask the form itself by passing in true.
7746      * @type Mixed
7747      */
7748     waitMsgTarget : false,
7749
7750     loadMask : true,
7751     
7752     /**
7753      * @cfg {Boolean} errorMask (true|false) default false
7754      */
7755     errorMask : false,
7756     
7757     /**
7758      * @cfg {Number} maskOffset Default 100
7759      */
7760     maskOffset : 100,
7761     
7762     /**
7763      * @cfg {Boolean} maskBody
7764      */
7765     maskBody : false,
7766
7767     getAutoCreate : function(){
7768
7769         var cfg = {
7770             tag: 'form',
7771             method : this.method || 'POST',
7772             id : this.id || Roo.id(),
7773             cls : ''
7774         };
7775         if (this.parent().xtype.match(/^Nav/)) {
7776             cfg.cls = 'navbar-form navbar-' + this.align;
7777
7778         }
7779
7780         if (this.labelAlign == 'left' ) {
7781             cfg.cls += ' form-horizontal';
7782         }
7783
7784
7785         return cfg;
7786     },
7787     initEvents : function()
7788     {
7789         this.el.on('submit', this.onSubmit, this);
7790         // this was added as random key presses on the form where triggering form submit.
7791         this.el.on('keypress', function(e) {
7792             if (e.getCharCode() != 13) {
7793                 return true;
7794             }
7795             // we might need to allow it for textareas.. and some other items.
7796             // check e.getTarget().
7797
7798             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7799                 return true;
7800             }
7801
7802             Roo.log("keypress blocked");
7803
7804             e.preventDefault();
7805             return false;
7806         });
7807         
7808     },
7809     // private
7810     onSubmit : function(e){
7811         e.stopEvent();
7812     },
7813
7814      /**
7815      * Returns true if client-side validation on the form is successful.
7816      * @return Boolean
7817      */
7818     isValid : function(){
7819         var items = this.getItems();
7820         var valid = true;
7821         var target = false;
7822         
7823         items.each(function(f){
7824             
7825             if(f.validate()){
7826                 return;
7827             }
7828             
7829             valid = false;
7830
7831             if(!target && f.el.isVisible(true)){
7832                 target = f;
7833             }
7834            
7835         });
7836         
7837         if(this.errorMask && !valid){
7838             Roo.bootstrap.Form.popover.mask(this, target);
7839         }
7840         
7841         return valid;
7842     },
7843     
7844     /**
7845      * Returns true if any fields in this form have changed since their original load.
7846      * @return Boolean
7847      */
7848     isDirty : function(){
7849         var dirty = false;
7850         var items = this.getItems();
7851         items.each(function(f){
7852            if(f.isDirty()){
7853                dirty = true;
7854                return false;
7855            }
7856            return true;
7857         });
7858         return dirty;
7859     },
7860      /**
7861      * Performs a predefined action (submit or load) or custom actions you define on this form.
7862      * @param {String} actionName The name of the action type
7863      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7864      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7865      * accept other config options):
7866      * <pre>
7867 Property          Type             Description
7868 ----------------  ---------------  ----------------------------------------------------------------------------------
7869 url               String           The url for the action (defaults to the form's url)
7870 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7871 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7872 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7873                                    validate the form on the client (defaults to false)
7874      * </pre>
7875      * @return {BasicForm} this
7876      */
7877     doAction : function(action, options){
7878         if(typeof action == 'string'){
7879             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7880         }
7881         if(this.fireEvent('beforeaction', this, action) !== false){
7882             this.beforeAction(action);
7883             action.run.defer(100, action);
7884         }
7885         return this;
7886     },
7887
7888     // private
7889     beforeAction : function(action){
7890         var o = action.options;
7891         
7892         if(this.loadMask){
7893             
7894             if(this.maskBody){
7895                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7896             } else {
7897                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7898             }
7899         }
7900         // not really supported yet.. ??
7901
7902         //if(this.waitMsgTarget === true){
7903         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7904         //}else if(this.waitMsgTarget){
7905         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7906         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7907         //}else {
7908         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7909        // }
7910
7911     },
7912
7913     // private
7914     afterAction : function(action, success){
7915         this.activeAction = null;
7916         var o = action.options;
7917
7918         if(this.loadMask){
7919             
7920             if(this.maskBody){
7921                 Roo.get(document.body).unmask();
7922             } else {
7923                 this.el.unmask();
7924             }
7925         }
7926         
7927         //if(this.waitMsgTarget === true){
7928 //            this.el.unmask();
7929         //}else if(this.waitMsgTarget){
7930         //    this.waitMsgTarget.unmask();
7931         //}else{
7932         //    Roo.MessageBox.updateProgress(1);
7933         //    Roo.MessageBox.hide();
7934        // }
7935         //
7936         if(success){
7937             if(o.reset){
7938                 this.reset();
7939             }
7940             Roo.callback(o.success, o.scope, [this, action]);
7941             this.fireEvent('actioncomplete', this, action);
7942
7943         }else{
7944
7945             // failure condition..
7946             // we have a scenario where updates need confirming.
7947             // eg. if a locking scenario exists..
7948             // we look for { errors : { needs_confirm : true }} in the response.
7949             if (
7950                 (typeof(action.result) != 'undefined')  &&
7951                 (typeof(action.result.errors) != 'undefined')  &&
7952                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7953            ){
7954                 var _t = this;
7955                 Roo.log("not supported yet");
7956                  /*
7957
7958                 Roo.MessageBox.confirm(
7959                     "Change requires confirmation",
7960                     action.result.errorMsg,
7961                     function(r) {
7962                         if (r != 'yes') {
7963                             return;
7964                         }
7965                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7966                     }
7967
7968                 );
7969                 */
7970
7971
7972                 return;
7973             }
7974
7975             Roo.callback(o.failure, o.scope, [this, action]);
7976             // show an error message if no failed handler is set..
7977             if (!this.hasListener('actionfailed')) {
7978                 Roo.log("need to add dialog support");
7979                 /*
7980                 Roo.MessageBox.alert("Error",
7981                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7982                         action.result.errorMsg :
7983                         "Saving Failed, please check your entries or try again"
7984                 );
7985                 */
7986             }
7987
7988             this.fireEvent('actionfailed', this, action);
7989         }
7990
7991     },
7992     /**
7993      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7994      * @param {String} id The value to search for
7995      * @return Field
7996      */
7997     findField : function(id){
7998         var items = this.getItems();
7999         var field = items.get(id);
8000         if(!field){
8001              items.each(function(f){
8002                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8003                     field = f;
8004                     return false;
8005                 }
8006                 return true;
8007             });
8008         }
8009         return field || null;
8010     },
8011      /**
8012      * Mark fields in this form invalid in bulk.
8013      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8014      * @return {BasicForm} this
8015      */
8016     markInvalid : function(errors){
8017         if(errors instanceof Array){
8018             for(var i = 0, len = errors.length; i < len; i++){
8019                 var fieldError = errors[i];
8020                 var f = this.findField(fieldError.id);
8021                 if(f){
8022                     f.markInvalid(fieldError.msg);
8023                 }
8024             }
8025         }else{
8026             var field, id;
8027             for(id in errors){
8028                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8029                     field.markInvalid(errors[id]);
8030                 }
8031             }
8032         }
8033         //Roo.each(this.childForms || [], function (f) {
8034         //    f.markInvalid(errors);
8035         //});
8036
8037         return this;
8038     },
8039
8040     /**
8041      * Set values for fields in this form in bulk.
8042      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8043      * @return {BasicForm} this
8044      */
8045     setValues : function(values){
8046         if(values instanceof Array){ // array of objects
8047             for(var i = 0, len = values.length; i < len; i++){
8048                 var v = values[i];
8049                 var f = this.findField(v.id);
8050                 if(f){
8051                     f.setValue(v.value);
8052                     if(this.trackResetOnLoad){
8053                         f.originalValue = f.getValue();
8054                     }
8055                 }
8056             }
8057         }else{ // object hash
8058             var field, id;
8059             for(id in values){
8060                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8061
8062                     if (field.setFromData &&
8063                         field.valueField &&
8064                         field.displayField &&
8065                         // combos' with local stores can
8066                         // be queried via setValue()
8067                         // to set their value..
8068                         (field.store && !field.store.isLocal)
8069                         ) {
8070                         // it's a combo
8071                         var sd = { };
8072                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8073                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8074                         field.setFromData(sd);
8075
8076                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8077                         
8078                         field.setFromData(values);
8079                         
8080                     } else {
8081                         field.setValue(values[id]);
8082                     }
8083
8084
8085                     if(this.trackResetOnLoad){
8086                         field.originalValue = field.getValue();
8087                     }
8088                 }
8089             }
8090         }
8091
8092         //Roo.each(this.childForms || [], function (f) {
8093         //    f.setValues(values);
8094         //});
8095
8096         return this;
8097     },
8098
8099     /**
8100      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8101      * they are returned as an array.
8102      * @param {Boolean} asString
8103      * @return {Object}
8104      */
8105     getValues : function(asString){
8106         //if (this.childForms) {
8107             // copy values from the child forms
8108         //    Roo.each(this.childForms, function (f) {
8109         //        this.setValues(f.getValues());
8110         //    }, this);
8111         //}
8112
8113
8114
8115         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8116         if(asString === true){
8117             return fs;
8118         }
8119         return Roo.urlDecode(fs);
8120     },
8121
8122     /**
8123      * Returns the fields in this form as an object with key/value pairs.
8124      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8125      * @return {Object}
8126      */
8127     getFieldValues : function(with_hidden)
8128     {
8129         var items = this.getItems();
8130         var ret = {};
8131         items.each(function(f){
8132             
8133             if (!f.getName()) {
8134                 return;
8135             }
8136             
8137             var v = f.getValue();
8138             
8139             if (f.inputType =='radio') {
8140                 if (typeof(ret[f.getName()]) == 'undefined') {
8141                     ret[f.getName()] = ''; // empty..
8142                 }
8143
8144                 if (!f.el.dom.checked) {
8145                     return;
8146
8147                 }
8148                 v = f.el.dom.value;
8149
8150             }
8151             
8152             if(f.xtype == 'MoneyField'){
8153                 ret[f.currencyName] = f.getCurrency();
8154             }
8155
8156             // not sure if this supported any more..
8157             if ((typeof(v) == 'object') && f.getRawValue) {
8158                 v = f.getRawValue() ; // dates..
8159             }
8160             // combo boxes where name != hiddenName...
8161             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8162                 ret[f.name] = f.getRawValue();
8163             }
8164             ret[f.getName()] = v;
8165         });
8166
8167         return ret;
8168     },
8169
8170     /**
8171      * Clears all invalid messages in this form.
8172      * @return {BasicForm} this
8173      */
8174     clearInvalid : function(){
8175         var items = this.getItems();
8176
8177         items.each(function(f){
8178            f.clearInvalid();
8179         });
8180
8181         return this;
8182     },
8183
8184     /**
8185      * Resets this form.
8186      * @return {BasicForm} this
8187      */
8188     reset : function(){
8189         var items = this.getItems();
8190         items.each(function(f){
8191             f.reset();
8192         });
8193
8194         Roo.each(this.childForms || [], function (f) {
8195             f.reset();
8196         });
8197
8198
8199         return this;
8200     },
8201     
8202     getItems : function()
8203     {
8204         var r=new Roo.util.MixedCollection(false, function(o){
8205             return o.id || (o.id = Roo.id());
8206         });
8207         var iter = function(el) {
8208             if (el.inputEl) {
8209                 r.add(el);
8210             }
8211             if (!el.items) {
8212                 return;
8213             }
8214             Roo.each(el.items,function(e) {
8215                 iter(e);
8216             });
8217         };
8218
8219         iter(this);
8220         return r;
8221     },
8222     
8223     hideFields : function(items)
8224     {
8225         Roo.each(items, function(i){
8226             
8227             var f = this.findField(i);
8228             
8229             if(!f){
8230                 return;
8231             }
8232             
8233             if(f.xtype == 'DateField'){
8234                 f.setVisible(false);
8235                 return;
8236             }
8237             
8238             f.hide();
8239             
8240         }, this);
8241     },
8242     
8243     showFields : function(items)
8244     {
8245         Roo.each(items, function(i){
8246             
8247             var f = this.findField(i);
8248             
8249             if(!f){
8250                 return;
8251             }
8252             
8253             if(f.xtype == 'DateField'){
8254                 f.setVisible(true);
8255                 return;
8256             }
8257             
8258             f.show();
8259             
8260         }, this);
8261     }
8262
8263 });
8264
8265 Roo.apply(Roo.bootstrap.Form, {
8266     
8267     popover : {
8268         
8269         padding : 5,
8270         
8271         isApplied : false,
8272         
8273         isMasked : false,
8274         
8275         form : false,
8276         
8277         target : false,
8278         
8279         toolTip : false,
8280         
8281         intervalID : false,
8282         
8283         maskEl : false,
8284         
8285         apply : function()
8286         {
8287             if(this.isApplied){
8288                 return;
8289             }
8290             
8291             this.maskEl = {
8292                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8293                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8294                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8295                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8296             };
8297             
8298             this.maskEl.top.enableDisplayMode("block");
8299             this.maskEl.left.enableDisplayMode("block");
8300             this.maskEl.bottom.enableDisplayMode("block");
8301             this.maskEl.right.enableDisplayMode("block");
8302             
8303             this.toolTip = new Roo.bootstrap.Tooltip({
8304                 cls : 'roo-form-error-popover',
8305                 alignment : {
8306                     'left' : ['r-l', [-2,0], 'right'],
8307                     'right' : ['l-r', [2,0], 'left'],
8308                     'bottom' : ['tl-bl', [0,2], 'top'],
8309                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8310                 }
8311             });
8312             
8313             this.toolTip.render(Roo.get(document.body));
8314
8315             this.toolTip.el.enableDisplayMode("block");
8316             
8317             Roo.get(document.body).on('click', function(){
8318                 this.unmask();
8319             }, this);
8320             
8321             Roo.get(document.body).on('touchstart', function(){
8322                 this.unmask();
8323             }, this);
8324             
8325             this.isApplied = true
8326         },
8327         
8328         mask : function(form, target)
8329         {
8330             this.form = form;
8331             
8332             this.target = target;
8333             
8334             if(!this.form.errorMask || !target.el){
8335                 return;
8336             }
8337             
8338             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8339             
8340             Roo.log(scrollable);
8341             
8342             var ot = this.target.el.calcOffsetsTo(scrollable);
8343             
8344             var scrollTo = ot[1] - this.form.maskOffset;
8345             
8346             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8347             
8348             scrollable.scrollTo('top', scrollTo);
8349             
8350             var box = this.target.el.getBox();
8351             Roo.log(box);
8352             var zIndex = Roo.bootstrap.Modal.zIndex++;
8353
8354             
8355             this.maskEl.top.setStyle('position', 'absolute');
8356             this.maskEl.top.setStyle('z-index', zIndex);
8357             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8358             this.maskEl.top.setLeft(0);
8359             this.maskEl.top.setTop(0);
8360             this.maskEl.top.show();
8361             
8362             this.maskEl.left.setStyle('position', 'absolute');
8363             this.maskEl.left.setStyle('z-index', zIndex);
8364             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8365             this.maskEl.left.setLeft(0);
8366             this.maskEl.left.setTop(box.y - this.padding);
8367             this.maskEl.left.show();
8368
8369             this.maskEl.bottom.setStyle('position', 'absolute');
8370             this.maskEl.bottom.setStyle('z-index', zIndex);
8371             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8372             this.maskEl.bottom.setLeft(0);
8373             this.maskEl.bottom.setTop(box.bottom + this.padding);
8374             this.maskEl.bottom.show();
8375
8376             this.maskEl.right.setStyle('position', 'absolute');
8377             this.maskEl.right.setStyle('z-index', zIndex);
8378             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8379             this.maskEl.right.setLeft(box.right + this.padding);
8380             this.maskEl.right.setTop(box.y - this.padding);
8381             this.maskEl.right.show();
8382
8383             this.toolTip.bindEl = this.target.el;
8384
8385             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8386
8387             var tip = this.target.blankText;
8388
8389             if(this.target.getValue() !== '' ) {
8390                 
8391                 if (this.target.invalidText.length) {
8392                     tip = this.target.invalidText;
8393                 } else if (this.target.regexText.length){
8394                     tip = this.target.regexText;
8395                 }
8396             }
8397
8398             this.toolTip.show(tip);
8399
8400             this.intervalID = window.setInterval(function() {
8401                 Roo.bootstrap.Form.popover.unmask();
8402             }, 10000);
8403
8404             window.onwheel = function(){ return false;};
8405             
8406             (function(){ this.isMasked = true; }).defer(500, this);
8407             
8408         },
8409         
8410         unmask : function()
8411         {
8412             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8413                 return;
8414             }
8415             
8416             this.maskEl.top.setStyle('position', 'absolute');
8417             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8418             this.maskEl.top.hide();
8419
8420             this.maskEl.left.setStyle('position', 'absolute');
8421             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8422             this.maskEl.left.hide();
8423
8424             this.maskEl.bottom.setStyle('position', 'absolute');
8425             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8426             this.maskEl.bottom.hide();
8427
8428             this.maskEl.right.setStyle('position', 'absolute');
8429             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8430             this.maskEl.right.hide();
8431             
8432             this.toolTip.hide();
8433             
8434             this.toolTip.el.hide();
8435             
8436             window.onwheel = function(){ return true;};
8437             
8438             if(this.intervalID){
8439                 window.clearInterval(this.intervalID);
8440                 this.intervalID = false;
8441             }
8442             
8443             this.isMasked = false;
8444             
8445         }
8446         
8447     }
8448     
8449 });
8450
8451 /*
8452  * Based on:
8453  * Ext JS Library 1.1.1
8454  * Copyright(c) 2006-2007, Ext JS, LLC.
8455  *
8456  * Originally Released Under LGPL - original licence link has changed is not relivant.
8457  *
8458  * Fork - LGPL
8459  * <script type="text/javascript">
8460  */
8461 /**
8462  * @class Roo.form.VTypes
8463  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8464  * @singleton
8465  */
8466 Roo.form.VTypes = function(){
8467     // closure these in so they are only created once.
8468     var alpha = /^[a-zA-Z_]+$/;
8469     var alphanum = /^[a-zA-Z0-9_]+$/;
8470     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8471     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8472
8473     // All these messages and functions are configurable
8474     return {
8475         /**
8476          * The function used to validate email addresses
8477          * @param {String} value The email address
8478          */
8479         'email' : function(v){
8480             return email.test(v);
8481         },
8482         /**
8483          * The error text to display when the email validation function returns false
8484          * @type String
8485          */
8486         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8487         /**
8488          * The keystroke filter mask to be applied on email input
8489          * @type RegExp
8490          */
8491         'emailMask' : /[a-z0-9_\.\-@]/i,
8492
8493         /**
8494          * The function used to validate URLs
8495          * @param {String} value The URL
8496          */
8497         'url' : function(v){
8498             return url.test(v);
8499         },
8500         /**
8501          * The error text to display when the url validation function returns false
8502          * @type String
8503          */
8504         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8505         
8506         /**
8507          * The function used to validate alpha values
8508          * @param {String} value The value
8509          */
8510         'alpha' : function(v){
8511             return alpha.test(v);
8512         },
8513         /**
8514          * The error text to display when the alpha validation function returns false
8515          * @type String
8516          */
8517         'alphaText' : 'This field should only contain letters and _',
8518         /**
8519          * The keystroke filter mask to be applied on alpha input
8520          * @type RegExp
8521          */
8522         'alphaMask' : /[a-z_]/i,
8523
8524         /**
8525          * The function used to validate alphanumeric values
8526          * @param {String} value The value
8527          */
8528         'alphanum' : function(v){
8529             return alphanum.test(v);
8530         },
8531         /**
8532          * The error text to display when the alphanumeric validation function returns false
8533          * @type String
8534          */
8535         'alphanumText' : 'This field should only contain letters, numbers and _',
8536         /**
8537          * The keystroke filter mask to be applied on alphanumeric input
8538          * @type RegExp
8539          */
8540         'alphanumMask' : /[a-z0-9_]/i
8541     };
8542 }();/*
8543  * - LGPL
8544  *
8545  * Input
8546  * 
8547  */
8548
8549 /**
8550  * @class Roo.bootstrap.Input
8551  * @extends Roo.bootstrap.Component
8552  * Bootstrap Input class
8553  * @cfg {Boolean} disabled is it disabled
8554  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8555  * @cfg {String} name name of the input
8556  * @cfg {string} fieldLabel - the label associated
8557  * @cfg {string} placeholder - placeholder to put in text.
8558  * @cfg {string}  before - input group add on before
8559  * @cfg {string} after - input group add on after
8560  * @cfg {string} size - (lg|sm) or leave empty..
8561  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8562  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8563  * @cfg {Number} md colspan out of 12 for computer-sized screens
8564  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8565  * @cfg {string} value default value of the input
8566  * @cfg {Number} labelWidth set the width of label 
8567  * @cfg {Number} labellg set the width of label (1-12)
8568  * @cfg {Number} labelmd set the width of label (1-12)
8569  * @cfg {Number} labelsm set the width of label (1-12)
8570  * @cfg {Number} labelxs set the width of label (1-12)
8571  * @cfg {String} labelAlign (top|left)
8572  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8573  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8574  * @cfg {String} indicatorpos (left|right) default left
8575  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8576  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8577
8578  * @cfg {String} align (left|center|right) Default left
8579  * @cfg {Boolean} forceFeedback (true|false) Default false
8580  * 
8581  * @constructor
8582  * Create a new Input
8583  * @param {Object} config The config object
8584  */
8585
8586 Roo.bootstrap.Input = function(config){
8587     
8588     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8589     
8590     this.addEvents({
8591         /**
8592          * @event focus
8593          * Fires when this field receives input focus.
8594          * @param {Roo.form.Field} this
8595          */
8596         focus : true,
8597         /**
8598          * @event blur
8599          * Fires when this field loses input focus.
8600          * @param {Roo.form.Field} this
8601          */
8602         blur : true,
8603         /**
8604          * @event specialkey
8605          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8606          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8607          * @param {Roo.form.Field} this
8608          * @param {Roo.EventObject} e The event object
8609          */
8610         specialkey : true,
8611         /**
8612          * @event change
8613          * Fires just before the field blurs if the field value has changed.
8614          * @param {Roo.form.Field} this
8615          * @param {Mixed} newValue The new value
8616          * @param {Mixed} oldValue The original value
8617          */
8618         change : true,
8619         /**
8620          * @event invalid
8621          * Fires after the field has been marked as invalid.
8622          * @param {Roo.form.Field} this
8623          * @param {String} msg The validation message
8624          */
8625         invalid : true,
8626         /**
8627          * @event valid
8628          * Fires after the field has been validated with no errors.
8629          * @param {Roo.form.Field} this
8630          */
8631         valid : true,
8632          /**
8633          * @event keyup
8634          * Fires after the key up
8635          * @param {Roo.form.Field} this
8636          * @param {Roo.EventObject}  e The event Object
8637          */
8638         keyup : true
8639     });
8640 };
8641
8642 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8643      /**
8644      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8645       automatic validation (defaults to "keyup").
8646      */
8647     validationEvent : "keyup",
8648      /**
8649      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8650      */
8651     validateOnBlur : true,
8652     /**
8653      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8654      */
8655     validationDelay : 250,
8656      /**
8657      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8658      */
8659     focusClass : "x-form-focus",  // not needed???
8660     
8661        
8662     /**
8663      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8664      */
8665     invalidClass : "has-warning",
8666     
8667     /**
8668      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8669      */
8670     validClass : "has-success",
8671     
8672     /**
8673      * @cfg {Boolean} hasFeedback (true|false) default true
8674      */
8675     hasFeedback : true,
8676     
8677     /**
8678      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8679      */
8680     invalidFeedbackClass : "glyphicon-warning-sign",
8681     
8682     /**
8683      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8684      */
8685     validFeedbackClass : "glyphicon-ok",
8686     
8687     /**
8688      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8689      */
8690     selectOnFocus : false,
8691     
8692      /**
8693      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8694      */
8695     maskRe : null,
8696        /**
8697      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8698      */
8699     vtype : null,
8700     
8701       /**
8702      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8703      */
8704     disableKeyFilter : false,
8705     
8706        /**
8707      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8708      */
8709     disabled : false,
8710      /**
8711      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8712      */
8713     allowBlank : true,
8714     /**
8715      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8716      */
8717     blankText : "Please complete this mandatory field",
8718     
8719      /**
8720      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8721      */
8722     minLength : 0,
8723     /**
8724      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8725      */
8726     maxLength : Number.MAX_VALUE,
8727     /**
8728      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8729      */
8730     minLengthText : "The minimum length for this field is {0}",
8731     /**
8732      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8733      */
8734     maxLengthText : "The maximum length for this field is {0}",
8735   
8736     
8737     /**
8738      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8739      * If available, this function will be called only after the basic validators all return true, and will be passed the
8740      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8741      */
8742     validator : null,
8743     /**
8744      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8745      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8746      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8747      */
8748     regex : null,
8749     /**
8750      * @cfg {String} regexText -- Depricated - use Invalid Text
8751      */
8752     regexText : "",
8753     
8754     /**
8755      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8756      */
8757     invalidText : "",
8758     
8759     
8760     
8761     autocomplete: false,
8762     
8763     
8764     fieldLabel : '',
8765     inputType : 'text',
8766     
8767     name : false,
8768     placeholder: false,
8769     before : false,
8770     after : false,
8771     size : false,
8772     hasFocus : false,
8773     preventMark: false,
8774     isFormField : true,
8775     value : '',
8776     labelWidth : 2,
8777     labelAlign : false,
8778     readOnly : false,
8779     align : false,
8780     formatedValue : false,
8781     forceFeedback : false,
8782     
8783     indicatorpos : 'left',
8784     
8785     labellg : 0,
8786     labelmd : 0,
8787     labelsm : 0,
8788     labelxs : 0,
8789     
8790     capture : '',
8791     accept : '',
8792     
8793     parentLabelAlign : function()
8794     {
8795         var parent = this;
8796         while (parent.parent()) {
8797             parent = parent.parent();
8798             if (typeof(parent.labelAlign) !='undefined') {
8799                 return parent.labelAlign;
8800             }
8801         }
8802         return 'left';
8803         
8804     },
8805     
8806     getAutoCreate : function()
8807     {
8808         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8809         
8810         var id = Roo.id();
8811         
8812         var cfg = {};
8813         
8814         if(this.inputType != 'hidden'){
8815             cfg.cls = 'form-group' //input-group
8816         }
8817         
8818         var input =  {
8819             tag: 'input',
8820             id : id,
8821             type : this.inputType,
8822             value : this.value,
8823             cls : 'form-control',
8824             placeholder : this.placeholder || '',
8825             autocomplete : this.autocomplete || 'new-password'
8826         };
8827         
8828         if(this.capture.length){
8829             input.capture = this.capture;
8830         }
8831         
8832         if(this.accept.length){
8833             input.accept = this.accept + "/*";
8834         }
8835         
8836         if(this.align){
8837             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8838         }
8839         
8840         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8841             input.maxLength = this.maxLength;
8842         }
8843         
8844         if (this.disabled) {
8845             input.disabled=true;
8846         }
8847         
8848         if (this.readOnly) {
8849             input.readonly=true;
8850         }
8851         
8852         if (this.name) {
8853             input.name = this.name;
8854         }
8855         
8856         if (this.size) {
8857             input.cls += ' input-' + this.size;
8858         }
8859         
8860         var settings=this;
8861         ['xs','sm','md','lg'].map(function(size){
8862             if (settings[size]) {
8863                 cfg.cls += ' col-' + size + '-' + settings[size];
8864             }
8865         });
8866         
8867         var inputblock = input;
8868         
8869         var feedback = {
8870             tag: 'span',
8871             cls: 'glyphicon form-control-feedback'
8872         };
8873             
8874         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8875             
8876             inputblock = {
8877                 cls : 'has-feedback',
8878                 cn :  [
8879                     input,
8880                     feedback
8881                 ] 
8882             };  
8883         }
8884         
8885         if (this.before || this.after) {
8886             
8887             inputblock = {
8888                 cls : 'input-group',
8889                 cn :  [] 
8890             };
8891             
8892             if (this.before && typeof(this.before) == 'string') {
8893                 
8894                 inputblock.cn.push({
8895                     tag :'span',
8896                     cls : 'roo-input-before input-group-addon',
8897                     html : this.before
8898                 });
8899             }
8900             if (this.before && typeof(this.before) == 'object') {
8901                 this.before = Roo.factory(this.before);
8902                 
8903                 inputblock.cn.push({
8904                     tag :'span',
8905                     cls : 'roo-input-before input-group-' +
8906                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8907                 });
8908             }
8909             
8910             inputblock.cn.push(input);
8911             
8912             if (this.after && typeof(this.after) == 'string') {
8913                 inputblock.cn.push({
8914                     tag :'span',
8915                     cls : 'roo-input-after input-group-addon',
8916                     html : this.after
8917                 });
8918             }
8919             if (this.after && typeof(this.after) == 'object') {
8920                 this.after = Roo.factory(this.after);
8921                 
8922                 inputblock.cn.push({
8923                     tag :'span',
8924                     cls : 'roo-input-after input-group-' +
8925                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8926                 });
8927             }
8928             
8929             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8930                 inputblock.cls += ' has-feedback';
8931                 inputblock.cn.push(feedback);
8932             }
8933         };
8934         
8935         if (align ==='left' && this.fieldLabel.length) {
8936             
8937             cfg.cls += ' roo-form-group-label-left';
8938             
8939             cfg.cn = [
8940                 {
8941                     tag : 'i',
8942                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8943                     tooltip : 'This field is required'
8944                 },
8945                 {
8946                     tag: 'label',
8947                     'for' :  id,
8948                     cls : 'control-label',
8949                     html : this.fieldLabel
8950
8951                 },
8952                 {
8953                     cls : "", 
8954                     cn: [
8955                         inputblock
8956                     ]
8957                 }
8958             ];
8959             
8960             var labelCfg = cfg.cn[1];
8961             var contentCfg = cfg.cn[2];
8962             
8963             if(this.indicatorpos == 'right'){
8964                 cfg.cn = [
8965                     {
8966                         tag: 'label',
8967                         'for' :  id,
8968                         cls : 'control-label',
8969                         cn : [
8970                             {
8971                                 tag : 'span',
8972                                 html : this.fieldLabel
8973                             },
8974                             {
8975                                 tag : 'i',
8976                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8977                                 tooltip : 'This field is required'
8978                             }
8979                         ]
8980                     },
8981                     {
8982                         cls : "",
8983                         cn: [
8984                             inputblock
8985                         ]
8986                     }
8987
8988                 ];
8989                 
8990                 labelCfg = cfg.cn[0];
8991                 contentCfg = cfg.cn[1];
8992             
8993             }
8994             
8995             if(this.labelWidth > 12){
8996                 labelCfg.style = "width: " + this.labelWidth + 'px';
8997             }
8998             
8999             if(this.labelWidth < 13 && this.labelmd == 0){
9000                 this.labelmd = this.labelWidth;
9001             }
9002             
9003             if(this.labellg > 0){
9004                 labelCfg.cls += ' col-lg-' + this.labellg;
9005                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9006             }
9007             
9008             if(this.labelmd > 0){
9009                 labelCfg.cls += ' col-md-' + this.labelmd;
9010                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9011             }
9012             
9013             if(this.labelsm > 0){
9014                 labelCfg.cls += ' col-sm-' + this.labelsm;
9015                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9016             }
9017             
9018             if(this.labelxs > 0){
9019                 labelCfg.cls += ' col-xs-' + this.labelxs;
9020                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9021             }
9022             
9023             
9024         } else if ( this.fieldLabel.length) {
9025                 
9026             cfg.cn = [
9027                 {
9028                     tag : 'i',
9029                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9030                     tooltip : 'This field is required'
9031                 },
9032                 {
9033                     tag: 'label',
9034                    //cls : 'input-group-addon',
9035                     html : this.fieldLabel
9036
9037                 },
9038
9039                inputblock
9040
9041            ];
9042            
9043            if(this.indicatorpos == 'right'){
9044                 
9045                 cfg.cn = [
9046                     {
9047                         tag: 'label',
9048                        //cls : 'input-group-addon',
9049                         html : this.fieldLabel
9050
9051                     },
9052                     {
9053                         tag : 'i',
9054                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9055                         tooltip : 'This field is required'
9056                     },
9057
9058                    inputblock
9059
9060                ];
9061
9062             }
9063
9064         } else {
9065             
9066             cfg.cn = [
9067
9068                     inputblock
9069
9070             ];
9071                 
9072                 
9073         };
9074         
9075         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9076            cfg.cls += ' navbar-form';
9077         }
9078         
9079         if (this.parentType === 'NavGroup') {
9080            cfg.cls += ' navbar-form';
9081            cfg.tag = 'li';
9082         }
9083         
9084         return cfg;
9085         
9086     },
9087     /**
9088      * return the real input element.
9089      */
9090     inputEl: function ()
9091     {
9092         return this.el.select('input.form-control',true).first();
9093     },
9094     
9095     tooltipEl : function()
9096     {
9097         return this.inputEl();
9098     },
9099     
9100     indicatorEl : function()
9101     {
9102         var indicator = this.el.select('i.roo-required-indicator',true).first();
9103         
9104         if(!indicator){
9105             return false;
9106         }
9107         
9108         return indicator;
9109         
9110     },
9111     
9112     setDisabled : function(v)
9113     {
9114         var i  = this.inputEl().dom;
9115         if (!v) {
9116             i.removeAttribute('disabled');
9117             return;
9118             
9119         }
9120         i.setAttribute('disabled','true');
9121     },
9122     initEvents : function()
9123     {
9124           
9125         this.inputEl().on("keydown" , this.fireKey,  this);
9126         this.inputEl().on("focus", this.onFocus,  this);
9127         this.inputEl().on("blur", this.onBlur,  this);
9128         
9129         this.inputEl().relayEvent('keyup', this);
9130         
9131         this.indicator = this.indicatorEl();
9132         
9133         if(this.indicator){
9134             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9135         }
9136  
9137         // reference to original value for reset
9138         this.originalValue = this.getValue();
9139         //Roo.form.TextField.superclass.initEvents.call(this);
9140         if(this.validationEvent == 'keyup'){
9141             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9142             this.inputEl().on('keyup', this.filterValidation, this);
9143         }
9144         else if(this.validationEvent !== false){
9145             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9146         }
9147         
9148         if(this.selectOnFocus){
9149             this.on("focus", this.preFocus, this);
9150             
9151         }
9152         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9153             this.inputEl().on("keypress", this.filterKeys, this);
9154         } else {
9155             this.inputEl().relayEvent('keypress', this);
9156         }
9157        /* if(this.grow){
9158             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9159             this.el.on("click", this.autoSize,  this);
9160         }
9161         */
9162         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9163             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9164         }
9165         
9166         if (typeof(this.before) == 'object') {
9167             this.before.render(this.el.select('.roo-input-before',true).first());
9168         }
9169         if (typeof(this.after) == 'object') {
9170             this.after.render(this.el.select('.roo-input-after',true).first());
9171         }
9172         
9173         this.inputEl().on('change', this.onChange, this);
9174         
9175     },
9176     filterValidation : function(e){
9177         if(!e.isNavKeyPress()){
9178             this.validationTask.delay(this.validationDelay);
9179         }
9180     },
9181      /**
9182      * Validates the field value
9183      * @return {Boolean} True if the value is valid, else false
9184      */
9185     validate : function(){
9186         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9187         if(this.disabled || this.validateValue(this.getRawValue())){
9188             this.markValid();
9189             return true;
9190         }
9191         
9192         this.markInvalid();
9193         return false;
9194     },
9195     
9196     
9197     /**
9198      * Validates a value according to the field's validation rules and marks the field as invalid
9199      * if the validation fails
9200      * @param {Mixed} value The value to validate
9201      * @return {Boolean} True if the value is valid, else false
9202      */
9203     validateValue : function(value)
9204     {
9205         if(this.getVisibilityEl().hasClass('hidden')){
9206             return true;
9207         }
9208         
9209         if(value.length < 1)  { // if it's blank
9210             if(this.allowBlank){
9211                 return true;
9212             }
9213             return false;
9214         }
9215         
9216         if(value.length < this.minLength){
9217             return false;
9218         }
9219         if(value.length > this.maxLength){
9220             return false;
9221         }
9222         if(this.vtype){
9223             var vt = Roo.form.VTypes;
9224             if(!vt[this.vtype](value, this)){
9225                 return false;
9226             }
9227         }
9228         if(typeof this.validator == "function"){
9229             var msg = this.validator(value);
9230             if(msg !== true){
9231                 return false;
9232             }
9233             if (typeof(msg) == 'string') {
9234                 this.invalidText = msg;
9235             }
9236         }
9237         
9238         if(this.regex && !this.regex.test(value)){
9239             return false;
9240         }
9241         
9242         return true;
9243     },
9244     
9245      // private
9246     fireKey : function(e){
9247         //Roo.log('field ' + e.getKey());
9248         if(e.isNavKeyPress()){
9249             this.fireEvent("specialkey", this, e);
9250         }
9251     },
9252     focus : function (selectText){
9253         if(this.rendered){
9254             this.inputEl().focus();
9255             if(selectText === true){
9256                 this.inputEl().dom.select();
9257             }
9258         }
9259         return this;
9260     } ,
9261     
9262     onFocus : function(){
9263         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9264            // this.el.addClass(this.focusClass);
9265         }
9266         if(!this.hasFocus){
9267             this.hasFocus = true;
9268             this.startValue = this.getValue();
9269             this.fireEvent("focus", this);
9270         }
9271     },
9272     
9273     beforeBlur : Roo.emptyFn,
9274
9275     
9276     // private
9277     onBlur : function(){
9278         this.beforeBlur();
9279         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9280             //this.el.removeClass(this.focusClass);
9281         }
9282         this.hasFocus = false;
9283         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9284             this.validate();
9285         }
9286         var v = this.getValue();
9287         if(String(v) !== String(this.startValue)){
9288             this.fireEvent('change', this, v, this.startValue);
9289         }
9290         this.fireEvent("blur", this);
9291     },
9292     
9293     onChange : function(e)
9294     {
9295         var v = this.getValue();
9296         if(String(v) !== String(this.startValue)){
9297             this.fireEvent('change', this, v, this.startValue);
9298         }
9299         
9300     },
9301     
9302     /**
9303      * Resets the current field value to the originally loaded value and clears any validation messages
9304      */
9305     reset : function(){
9306         this.setValue(this.originalValue);
9307         this.validate();
9308     },
9309      /**
9310      * Returns the name of the field
9311      * @return {Mixed} name The name field
9312      */
9313     getName: function(){
9314         return this.name;
9315     },
9316      /**
9317      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9318      * @return {Mixed} value The field value
9319      */
9320     getValue : function(){
9321         
9322         var v = this.inputEl().getValue();
9323         
9324         return v;
9325     },
9326     /**
9327      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9328      * @return {Mixed} value The field value
9329      */
9330     getRawValue : function(){
9331         var v = this.inputEl().getValue();
9332         
9333         return v;
9334     },
9335     
9336     /**
9337      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9338      * @param {Mixed} value The value to set
9339      */
9340     setRawValue : function(v){
9341         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9342     },
9343     
9344     selectText : function(start, end){
9345         var v = this.getRawValue();
9346         if(v.length > 0){
9347             start = start === undefined ? 0 : start;
9348             end = end === undefined ? v.length : end;
9349             var d = this.inputEl().dom;
9350             if(d.setSelectionRange){
9351                 d.setSelectionRange(start, end);
9352             }else if(d.createTextRange){
9353                 var range = d.createTextRange();
9354                 range.moveStart("character", start);
9355                 range.moveEnd("character", v.length-end);
9356                 range.select();
9357             }
9358         }
9359     },
9360     
9361     /**
9362      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9363      * @param {Mixed} value The value to set
9364      */
9365     setValue : function(v){
9366         this.value = v;
9367         if(this.rendered){
9368             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9369             this.validate();
9370         }
9371     },
9372     
9373     /*
9374     processValue : function(value){
9375         if(this.stripCharsRe){
9376             var newValue = value.replace(this.stripCharsRe, '');
9377             if(newValue !== value){
9378                 this.setRawValue(newValue);
9379                 return newValue;
9380             }
9381         }
9382         return value;
9383     },
9384   */
9385     preFocus : function(){
9386         
9387         if(this.selectOnFocus){
9388             this.inputEl().dom.select();
9389         }
9390     },
9391     filterKeys : function(e){
9392         var k = e.getKey();
9393         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9394             return;
9395         }
9396         var c = e.getCharCode(), cc = String.fromCharCode(c);
9397         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9398             return;
9399         }
9400         if(!this.maskRe.test(cc)){
9401             e.stopEvent();
9402         }
9403     },
9404      /**
9405      * Clear any invalid styles/messages for this field
9406      */
9407     clearInvalid : function(){
9408         
9409         if(!this.el || this.preventMark){ // not rendered
9410             return;
9411         }
9412         
9413      
9414         this.el.removeClass(this.invalidClass);
9415         
9416         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9417             
9418             var feedback = this.el.select('.form-control-feedback', true).first();
9419             
9420             if(feedback){
9421                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9422             }
9423             
9424         }
9425         
9426         if(this.indicator){
9427             this.indicator.removeClass('visible');
9428             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9429         }
9430         
9431         this.fireEvent('valid', this);
9432     },
9433     
9434      /**
9435      * Mark this field as valid
9436      */
9437     markValid : function()
9438     {
9439         if(!this.el  || this.preventMark){ // not rendered...
9440             return;
9441         }
9442         
9443         this.el.removeClass([this.invalidClass, this.validClass]);
9444         
9445         var feedback = this.el.select('.form-control-feedback', true).first();
9446             
9447         if(feedback){
9448             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9449         }
9450         
9451         if(this.indicator){
9452             this.indicator.removeClass('visible');
9453             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9454         }
9455         
9456         if(this.disabled){
9457             return;
9458         }
9459         
9460         if(this.allowBlank && !this.getRawValue().length){
9461             return;
9462         }
9463         
9464         this.el.addClass(this.validClass);
9465         
9466         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9467             
9468             var feedback = this.el.select('.form-control-feedback', true).first();
9469             
9470             if(feedback){
9471                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9472                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9473             }
9474             
9475         }
9476         
9477         this.fireEvent('valid', this);
9478     },
9479     
9480      /**
9481      * Mark this field as invalid
9482      * @param {String} msg The validation message
9483      */
9484     markInvalid : function(msg)
9485     {
9486         if(!this.el  || this.preventMark){ // not rendered
9487             return;
9488         }
9489         
9490         this.el.removeClass([this.invalidClass, this.validClass]);
9491         
9492         var feedback = this.el.select('.form-control-feedback', true).first();
9493             
9494         if(feedback){
9495             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9496         }
9497
9498         if(this.disabled){
9499             return;
9500         }
9501         
9502         if(this.allowBlank && !this.getRawValue().length){
9503             return;
9504         }
9505         
9506         if(this.indicator){
9507             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9508             this.indicator.addClass('visible');
9509         }
9510         
9511         this.el.addClass(this.invalidClass);
9512         
9513         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9514             
9515             var feedback = this.el.select('.form-control-feedback', true).first();
9516             
9517             if(feedback){
9518                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9519                 
9520                 if(this.getValue().length || this.forceFeedback){
9521                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9522                 }
9523                 
9524             }
9525             
9526         }
9527         
9528         this.fireEvent('invalid', this, msg);
9529     },
9530     // private
9531     SafariOnKeyDown : function(event)
9532     {
9533         // this is a workaround for a password hang bug on chrome/ webkit.
9534         if (this.inputEl().dom.type != 'password') {
9535             return;
9536         }
9537         
9538         var isSelectAll = false;
9539         
9540         if(this.inputEl().dom.selectionEnd > 0){
9541             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9542         }
9543         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9544             event.preventDefault();
9545             this.setValue('');
9546             return;
9547         }
9548         
9549         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9550             
9551             event.preventDefault();
9552             // this is very hacky as keydown always get's upper case.
9553             //
9554             var cc = String.fromCharCode(event.getCharCode());
9555             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9556             
9557         }
9558     },
9559     adjustWidth : function(tag, w){
9560         tag = tag.toLowerCase();
9561         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9562             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9563                 if(tag == 'input'){
9564                     return w + 2;
9565                 }
9566                 if(tag == 'textarea'){
9567                     return w-2;
9568                 }
9569             }else if(Roo.isOpera){
9570                 if(tag == 'input'){
9571                     return w + 2;
9572                 }
9573                 if(tag == 'textarea'){
9574                     return w-2;
9575                 }
9576             }
9577         }
9578         return w;
9579     },
9580     
9581     setFieldLabel : function(v)
9582     {
9583         if(!this.rendered){
9584             return;
9585         }
9586         
9587         if(this.indicator){
9588             var ar = this.el.select('label > span',true);
9589             
9590             if (ar.elements.length) {
9591                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9592                 this.fieldLabel = v;
9593                 return;
9594             }
9595             
9596             var br = this.el.select('label',true);
9597             
9598             if(br.elements.length) {
9599                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9600                 this.fieldLabel = v;
9601                 return;
9602             }
9603             
9604             Roo.log('Cannot Found any of label > span || label in input');
9605             return;
9606         }
9607         
9608         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9609         this.fieldLabel = v;
9610         
9611         
9612     }
9613 });
9614
9615  
9616 /*
9617  * - LGPL
9618  *
9619  * Input
9620  * 
9621  */
9622
9623 /**
9624  * @class Roo.bootstrap.TextArea
9625  * @extends Roo.bootstrap.Input
9626  * Bootstrap TextArea class
9627  * @cfg {Number} cols Specifies the visible width of a text area
9628  * @cfg {Number} rows Specifies the visible number of lines in a text area
9629  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9630  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9631  * @cfg {string} html text
9632  * 
9633  * @constructor
9634  * Create a new TextArea
9635  * @param {Object} config The config object
9636  */
9637
9638 Roo.bootstrap.TextArea = function(config){
9639     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9640    
9641 };
9642
9643 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9644      
9645     cols : false,
9646     rows : 5,
9647     readOnly : false,
9648     warp : 'soft',
9649     resize : false,
9650     value: false,
9651     html: false,
9652     
9653     getAutoCreate : function(){
9654         
9655         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9656         
9657         var id = Roo.id();
9658         
9659         var cfg = {};
9660         
9661         if(this.inputType != 'hidden'){
9662             cfg.cls = 'form-group' //input-group
9663         }
9664         
9665         var input =  {
9666             tag: 'textarea',
9667             id : id,
9668             warp : this.warp,
9669             rows : this.rows,
9670             value : this.value || '',
9671             html: this.html || '',
9672             cls : 'form-control',
9673             placeholder : this.placeholder || '' 
9674             
9675         };
9676         
9677         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9678             input.maxLength = this.maxLength;
9679         }
9680         
9681         if(this.resize){
9682             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9683         }
9684         
9685         if(this.cols){
9686             input.cols = this.cols;
9687         }
9688         
9689         if (this.readOnly) {
9690             input.readonly = true;
9691         }
9692         
9693         if (this.name) {
9694             input.name = this.name;
9695         }
9696         
9697         if (this.size) {
9698             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9699         }
9700         
9701         var settings=this;
9702         ['xs','sm','md','lg'].map(function(size){
9703             if (settings[size]) {
9704                 cfg.cls += ' col-' + size + '-' + settings[size];
9705             }
9706         });
9707         
9708         var inputblock = input;
9709         
9710         if(this.hasFeedback && !this.allowBlank){
9711             
9712             var feedback = {
9713                 tag: 'span',
9714                 cls: 'glyphicon form-control-feedback'
9715             };
9716
9717             inputblock = {
9718                 cls : 'has-feedback',
9719                 cn :  [
9720                     input,
9721                     feedback
9722                 ] 
9723             };  
9724         }
9725         
9726         
9727         if (this.before || this.after) {
9728             
9729             inputblock = {
9730                 cls : 'input-group',
9731                 cn :  [] 
9732             };
9733             if (this.before) {
9734                 inputblock.cn.push({
9735                     tag :'span',
9736                     cls : 'input-group-addon',
9737                     html : this.before
9738                 });
9739             }
9740             
9741             inputblock.cn.push(input);
9742             
9743             if(this.hasFeedback && !this.allowBlank){
9744                 inputblock.cls += ' has-feedback';
9745                 inputblock.cn.push(feedback);
9746             }
9747             
9748             if (this.after) {
9749                 inputblock.cn.push({
9750                     tag :'span',
9751                     cls : 'input-group-addon',
9752                     html : this.after
9753                 });
9754             }
9755             
9756         }
9757         
9758         if (align ==='left' && this.fieldLabel.length) {
9759             cfg.cn = [
9760                 {
9761                     tag: 'label',
9762                     'for' :  id,
9763                     cls : 'control-label',
9764                     html : this.fieldLabel
9765                 },
9766                 {
9767                     cls : "",
9768                     cn: [
9769                         inputblock
9770                     ]
9771                 }
9772
9773             ];
9774             
9775             if(this.labelWidth > 12){
9776                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9777             }
9778
9779             if(this.labelWidth < 13 && this.labelmd == 0){
9780                 this.labelmd = this.labelWidth;
9781             }
9782
9783             if(this.labellg > 0){
9784                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9785                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9786             }
9787
9788             if(this.labelmd > 0){
9789                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9790                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9791             }
9792
9793             if(this.labelsm > 0){
9794                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9795                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9796             }
9797
9798             if(this.labelxs > 0){
9799                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9800                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9801             }
9802             
9803         } else if ( this.fieldLabel.length) {
9804             cfg.cn = [
9805
9806                {
9807                    tag: 'label',
9808                    //cls : 'input-group-addon',
9809                    html : this.fieldLabel
9810
9811                },
9812
9813                inputblock
9814
9815            ];
9816
9817         } else {
9818
9819             cfg.cn = [
9820
9821                 inputblock
9822
9823             ];
9824                 
9825         }
9826         
9827         if (this.disabled) {
9828             input.disabled=true;
9829         }
9830         
9831         return cfg;
9832         
9833     },
9834     /**
9835      * return the real textarea element.
9836      */
9837     inputEl: function ()
9838     {
9839         return this.el.select('textarea.form-control',true).first();
9840     },
9841     
9842     /**
9843      * Clear any invalid styles/messages for this field
9844      */
9845     clearInvalid : function()
9846     {
9847         
9848         if(!this.el || this.preventMark){ // not rendered
9849             return;
9850         }
9851         
9852         var label = this.el.select('label', true).first();
9853         var icon = this.el.select('i.fa-star', true).first();
9854         
9855         if(label && icon){
9856             icon.remove();
9857         }
9858         
9859         this.el.removeClass(this.invalidClass);
9860         
9861         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9862             
9863             var feedback = this.el.select('.form-control-feedback', true).first();
9864             
9865             if(feedback){
9866                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9867             }
9868             
9869         }
9870         
9871         this.fireEvent('valid', this);
9872     },
9873     
9874      /**
9875      * Mark this field as valid
9876      */
9877     markValid : function()
9878     {
9879         if(!this.el  || this.preventMark){ // not rendered
9880             return;
9881         }
9882         
9883         this.el.removeClass([this.invalidClass, this.validClass]);
9884         
9885         var feedback = this.el.select('.form-control-feedback', true).first();
9886             
9887         if(feedback){
9888             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9889         }
9890
9891         if(this.disabled || this.allowBlank){
9892             return;
9893         }
9894         
9895         var label = this.el.select('label', true).first();
9896         var icon = this.el.select('i.fa-star', true).first();
9897         
9898         if(label && icon){
9899             icon.remove();
9900         }
9901         
9902         this.el.addClass(this.validClass);
9903         
9904         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9905             
9906             var feedback = this.el.select('.form-control-feedback', true).first();
9907             
9908             if(feedback){
9909                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9910                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9911             }
9912             
9913         }
9914         
9915         this.fireEvent('valid', this);
9916     },
9917     
9918      /**
9919      * Mark this field as invalid
9920      * @param {String} msg The validation message
9921      */
9922     markInvalid : function(msg)
9923     {
9924         if(!this.el  || this.preventMark){ // not rendered
9925             return;
9926         }
9927         
9928         this.el.removeClass([this.invalidClass, this.validClass]);
9929         
9930         var feedback = this.el.select('.form-control-feedback', true).first();
9931             
9932         if(feedback){
9933             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9934         }
9935
9936         if(this.disabled || this.allowBlank){
9937             return;
9938         }
9939         
9940         var label = this.el.select('label', true).first();
9941         var icon = this.el.select('i.fa-star', true).first();
9942         
9943         if(!this.getValue().length && label && !icon){
9944             this.el.createChild({
9945                 tag : 'i',
9946                 cls : 'text-danger fa fa-lg fa-star',
9947                 tooltip : 'This field is required',
9948                 style : 'margin-right:5px;'
9949             }, label, true);
9950         }
9951
9952         this.el.addClass(this.invalidClass);
9953         
9954         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9955             
9956             var feedback = this.el.select('.form-control-feedback', true).first();
9957             
9958             if(feedback){
9959                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9960                 
9961                 if(this.getValue().length || this.forceFeedback){
9962                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9963                 }
9964                 
9965             }
9966             
9967         }
9968         
9969         this.fireEvent('invalid', this, msg);
9970     }
9971 });
9972
9973  
9974 /*
9975  * - LGPL
9976  *
9977  * trigger field - base class for combo..
9978  * 
9979  */
9980  
9981 /**
9982  * @class Roo.bootstrap.TriggerField
9983  * @extends Roo.bootstrap.Input
9984  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9985  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9986  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9987  * for which you can provide a custom implementation.  For example:
9988  * <pre><code>
9989 var trigger = new Roo.bootstrap.TriggerField();
9990 trigger.onTriggerClick = myTriggerFn;
9991 trigger.applyTo('my-field');
9992 </code></pre>
9993  *
9994  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9995  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9996  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9997  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9998  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9999
10000  * @constructor
10001  * Create a new TriggerField.
10002  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10003  * to the base TextField)
10004  */
10005 Roo.bootstrap.TriggerField = function(config){
10006     this.mimicing = false;
10007     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10008 };
10009
10010 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10011     /**
10012      * @cfg {String} triggerClass A CSS class to apply to the trigger
10013      */
10014      /**
10015      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10016      */
10017     hideTrigger:false,
10018
10019     /**
10020      * @cfg {Boolean} removable (true|false) special filter default false
10021      */
10022     removable : false,
10023     
10024     /** @cfg {Boolean} grow @hide */
10025     /** @cfg {Number} growMin @hide */
10026     /** @cfg {Number} growMax @hide */
10027
10028     /**
10029      * @hide 
10030      * @method
10031      */
10032     autoSize: Roo.emptyFn,
10033     // private
10034     monitorTab : true,
10035     // private
10036     deferHeight : true,
10037
10038     
10039     actionMode : 'wrap',
10040     
10041     caret : false,
10042     
10043     
10044     getAutoCreate : function(){
10045        
10046         var align = this.labelAlign || this.parentLabelAlign();
10047         
10048         var id = Roo.id();
10049         
10050         var cfg = {
10051             cls: 'form-group' //input-group
10052         };
10053         
10054         
10055         var input =  {
10056             tag: 'input',
10057             id : id,
10058             type : this.inputType,
10059             cls : 'form-control',
10060             autocomplete: 'new-password',
10061             placeholder : this.placeholder || '' 
10062             
10063         };
10064         if (this.name) {
10065             input.name = this.name;
10066         }
10067         if (this.size) {
10068             input.cls += ' input-' + this.size;
10069         }
10070         
10071         if (this.disabled) {
10072             input.disabled=true;
10073         }
10074         
10075         var inputblock = input;
10076         
10077         if(this.hasFeedback && !this.allowBlank){
10078             
10079             var feedback = {
10080                 tag: 'span',
10081                 cls: 'glyphicon form-control-feedback'
10082             };
10083             
10084             if(this.removable && !this.editable && !this.tickable){
10085                 inputblock = {
10086                     cls : 'has-feedback',
10087                     cn :  [
10088                         inputblock,
10089                         {
10090                             tag: 'button',
10091                             html : 'x',
10092                             cls : 'roo-combo-removable-btn close'
10093                         },
10094                         feedback
10095                     ] 
10096                 };
10097             } else {
10098                 inputblock = {
10099                     cls : 'has-feedback',
10100                     cn :  [
10101                         inputblock,
10102                         feedback
10103                     ] 
10104                 };
10105             }
10106
10107         } else {
10108             if(this.removable && !this.editable && !this.tickable){
10109                 inputblock = {
10110                     cls : 'roo-removable',
10111                     cn :  [
10112                         inputblock,
10113                         {
10114                             tag: 'button',
10115                             html : 'x',
10116                             cls : 'roo-combo-removable-btn close'
10117                         }
10118                     ] 
10119                 };
10120             }
10121         }
10122         
10123         if (this.before || this.after) {
10124             
10125             inputblock = {
10126                 cls : 'input-group',
10127                 cn :  [] 
10128             };
10129             if (this.before) {
10130                 inputblock.cn.push({
10131                     tag :'span',
10132                     cls : 'input-group-addon',
10133                     html : this.before
10134                 });
10135             }
10136             
10137             inputblock.cn.push(input);
10138             
10139             if(this.hasFeedback && !this.allowBlank){
10140                 inputblock.cls += ' has-feedback';
10141                 inputblock.cn.push(feedback);
10142             }
10143             
10144             if (this.after) {
10145                 inputblock.cn.push({
10146                     tag :'span',
10147                     cls : 'input-group-addon',
10148                     html : this.after
10149                 });
10150             }
10151             
10152         };
10153         
10154         var box = {
10155             tag: 'div',
10156             cn: [
10157                 {
10158                     tag: 'input',
10159                     type : 'hidden',
10160                     cls: 'form-hidden-field'
10161                 },
10162                 inputblock
10163             ]
10164             
10165         };
10166         
10167         if(this.multiple){
10168             box = {
10169                 tag: 'div',
10170                 cn: [
10171                     {
10172                         tag: 'input',
10173                         type : 'hidden',
10174                         cls: 'form-hidden-field'
10175                     },
10176                     {
10177                         tag: 'ul',
10178                         cls: 'roo-select2-choices',
10179                         cn:[
10180                             {
10181                                 tag: 'li',
10182                                 cls: 'roo-select2-search-field',
10183                                 cn: [
10184
10185                                     inputblock
10186                                 ]
10187                             }
10188                         ]
10189                     }
10190                 ]
10191             }
10192         };
10193         
10194         var combobox = {
10195             cls: 'roo-select2-container input-group',
10196             cn: [
10197                 box
10198 //                {
10199 //                    tag: 'ul',
10200 //                    cls: 'typeahead typeahead-long dropdown-menu',
10201 //                    style: 'display:none'
10202 //                }
10203             ]
10204         };
10205         
10206         if(!this.multiple && this.showToggleBtn){
10207             
10208             var caret = {
10209                         tag: 'span',
10210                         cls: 'caret'
10211              };
10212             if (this.caret != false) {
10213                 caret = {
10214                      tag: 'i',
10215                      cls: 'fa fa-' + this.caret
10216                 };
10217                 
10218             }
10219             
10220             combobox.cn.push({
10221                 tag :'span',
10222                 cls : 'input-group-addon btn dropdown-toggle',
10223                 cn : [
10224                     caret,
10225                     {
10226                         tag: 'span',
10227                         cls: 'combobox-clear',
10228                         cn  : [
10229                             {
10230                                 tag : 'i',
10231                                 cls: 'icon-remove'
10232                             }
10233                         ]
10234                     }
10235                 ]
10236
10237             })
10238         }
10239         
10240         if(this.multiple){
10241             combobox.cls += ' roo-select2-container-multi';
10242         }
10243         
10244         if (align ==='left' && this.fieldLabel.length) {
10245             
10246             cfg.cls += ' roo-form-group-label-left';
10247
10248             cfg.cn = [
10249                 {
10250                     tag : 'i',
10251                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10252                     tooltip : 'This field is required'
10253                 },
10254                 {
10255                     tag: 'label',
10256                     'for' :  id,
10257                     cls : 'control-label',
10258                     html : this.fieldLabel
10259
10260                 },
10261                 {
10262                     cls : "", 
10263                     cn: [
10264                         combobox
10265                     ]
10266                 }
10267
10268             ];
10269             
10270             var labelCfg = cfg.cn[1];
10271             var contentCfg = cfg.cn[2];
10272             
10273             if(this.indicatorpos == 'right'){
10274                 cfg.cn = [
10275                     {
10276                         tag: 'label',
10277                         'for' :  id,
10278                         cls : 'control-label',
10279                         cn : [
10280                             {
10281                                 tag : 'span',
10282                                 html : this.fieldLabel
10283                             },
10284                             {
10285                                 tag : 'i',
10286                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10287                                 tooltip : 'This field is required'
10288                             }
10289                         ]
10290                     },
10291                     {
10292                         cls : "", 
10293                         cn: [
10294                             combobox
10295                         ]
10296                     }
10297
10298                 ];
10299                 
10300                 labelCfg = cfg.cn[0];
10301                 contentCfg = cfg.cn[1];
10302             }
10303             
10304             if(this.labelWidth > 12){
10305                 labelCfg.style = "width: " + this.labelWidth + 'px';
10306             }
10307             
10308             if(this.labelWidth < 13 && this.labelmd == 0){
10309                 this.labelmd = this.labelWidth;
10310             }
10311             
10312             if(this.labellg > 0){
10313                 labelCfg.cls += ' col-lg-' + this.labellg;
10314                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10315             }
10316             
10317             if(this.labelmd > 0){
10318                 labelCfg.cls += ' col-md-' + this.labelmd;
10319                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10320             }
10321             
10322             if(this.labelsm > 0){
10323                 labelCfg.cls += ' col-sm-' + this.labelsm;
10324                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10325             }
10326             
10327             if(this.labelxs > 0){
10328                 labelCfg.cls += ' col-xs-' + this.labelxs;
10329                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10330             }
10331             
10332         } else if ( this.fieldLabel.length) {
10333 //                Roo.log(" label");
10334             cfg.cn = [
10335                 {
10336                    tag : 'i',
10337                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10338                    tooltip : 'This field is required'
10339                },
10340                {
10341                    tag: 'label',
10342                    //cls : 'input-group-addon',
10343                    html : this.fieldLabel
10344
10345                },
10346
10347                combobox
10348
10349             ];
10350             
10351             if(this.indicatorpos == 'right'){
10352                 
10353                 cfg.cn = [
10354                     {
10355                        tag: 'label',
10356                        cn : [
10357                            {
10358                                tag : 'span',
10359                                html : this.fieldLabel
10360                            },
10361                            {
10362                               tag : 'i',
10363                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10364                               tooltip : 'This field is required'
10365                            }
10366                        ]
10367
10368                     },
10369                     combobox
10370
10371                 ];
10372
10373             }
10374
10375         } else {
10376             
10377 //                Roo.log(" no label && no align");
10378                 cfg = combobox
10379                      
10380                 
10381         }
10382         
10383         var settings=this;
10384         ['xs','sm','md','lg'].map(function(size){
10385             if (settings[size]) {
10386                 cfg.cls += ' col-' + size + '-' + settings[size];
10387             }
10388         });
10389         
10390         return cfg;
10391         
10392     },
10393     
10394     
10395     
10396     // private
10397     onResize : function(w, h){
10398 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10399 //        if(typeof w == 'number'){
10400 //            var x = w - this.trigger.getWidth();
10401 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10402 //            this.trigger.setStyle('left', x+'px');
10403 //        }
10404     },
10405
10406     // private
10407     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10408
10409     // private
10410     getResizeEl : function(){
10411         return this.inputEl();
10412     },
10413
10414     // private
10415     getPositionEl : function(){
10416         return this.inputEl();
10417     },
10418
10419     // private
10420     alignErrorIcon : function(){
10421         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10422     },
10423
10424     // private
10425     initEvents : function(){
10426         
10427         this.createList();
10428         
10429         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10430         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10431         if(!this.multiple && this.showToggleBtn){
10432             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10433             if(this.hideTrigger){
10434                 this.trigger.setDisplayed(false);
10435             }
10436             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10437         }
10438         
10439         if(this.multiple){
10440             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10441         }
10442         
10443         if(this.removable && !this.editable && !this.tickable){
10444             var close = this.closeTriggerEl();
10445             
10446             if(close){
10447                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10448                 close.on('click', this.removeBtnClick, this, close);
10449             }
10450         }
10451         
10452         //this.trigger.addClassOnOver('x-form-trigger-over');
10453         //this.trigger.addClassOnClick('x-form-trigger-click');
10454         
10455         //if(!this.width){
10456         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10457         //}
10458     },
10459     
10460     closeTriggerEl : function()
10461     {
10462         var close = this.el.select('.roo-combo-removable-btn', true).first();
10463         return close ? close : false;
10464     },
10465     
10466     removeBtnClick : function(e, h, el)
10467     {
10468         e.preventDefault();
10469         
10470         if(this.fireEvent("remove", this) !== false){
10471             this.reset();
10472             this.fireEvent("afterremove", this)
10473         }
10474     },
10475     
10476     createList : function()
10477     {
10478         this.list = Roo.get(document.body).createChild({
10479             tag: 'ul',
10480             cls: 'typeahead typeahead-long dropdown-menu',
10481             style: 'display:none'
10482         });
10483         
10484         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10485         
10486     },
10487
10488     // private
10489     initTrigger : function(){
10490        
10491     },
10492
10493     // private
10494     onDestroy : function(){
10495         if(this.trigger){
10496             this.trigger.removeAllListeners();
10497           //  this.trigger.remove();
10498         }
10499         //if(this.wrap){
10500         //    this.wrap.remove();
10501         //}
10502         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10503     },
10504
10505     // private
10506     onFocus : function(){
10507         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10508         /*
10509         if(!this.mimicing){
10510             this.wrap.addClass('x-trigger-wrap-focus');
10511             this.mimicing = true;
10512             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10513             if(this.monitorTab){
10514                 this.el.on("keydown", this.checkTab, this);
10515             }
10516         }
10517         */
10518     },
10519
10520     // private
10521     checkTab : function(e){
10522         if(e.getKey() == e.TAB){
10523             this.triggerBlur();
10524         }
10525     },
10526
10527     // private
10528     onBlur : function(){
10529         // do nothing
10530     },
10531
10532     // private
10533     mimicBlur : function(e, t){
10534         /*
10535         if(!this.wrap.contains(t) && this.validateBlur()){
10536             this.triggerBlur();
10537         }
10538         */
10539     },
10540
10541     // private
10542     triggerBlur : function(){
10543         this.mimicing = false;
10544         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10545         if(this.monitorTab){
10546             this.el.un("keydown", this.checkTab, this);
10547         }
10548         //this.wrap.removeClass('x-trigger-wrap-focus');
10549         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10550     },
10551
10552     // private
10553     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10554     validateBlur : function(e, t){
10555         return true;
10556     },
10557
10558     // private
10559     onDisable : function(){
10560         this.inputEl().dom.disabled = true;
10561         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10562         //if(this.wrap){
10563         //    this.wrap.addClass('x-item-disabled');
10564         //}
10565     },
10566
10567     // private
10568     onEnable : function(){
10569         this.inputEl().dom.disabled = false;
10570         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10571         //if(this.wrap){
10572         //    this.el.removeClass('x-item-disabled');
10573         //}
10574     },
10575
10576     // private
10577     onShow : function(){
10578         var ae = this.getActionEl();
10579         
10580         if(ae){
10581             ae.dom.style.display = '';
10582             ae.dom.style.visibility = 'visible';
10583         }
10584     },
10585
10586     // private
10587     
10588     onHide : function(){
10589         var ae = this.getActionEl();
10590         ae.dom.style.display = 'none';
10591     },
10592
10593     /**
10594      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10595      * by an implementing function.
10596      * @method
10597      * @param {EventObject} e
10598      */
10599     onTriggerClick : Roo.emptyFn
10600 });
10601  /*
10602  * Based on:
10603  * Ext JS Library 1.1.1
10604  * Copyright(c) 2006-2007, Ext JS, LLC.
10605  *
10606  * Originally Released Under LGPL - original licence link has changed is not relivant.
10607  *
10608  * Fork - LGPL
10609  * <script type="text/javascript">
10610  */
10611
10612
10613 /**
10614  * @class Roo.data.SortTypes
10615  * @singleton
10616  * Defines the default sorting (casting?) comparison functions used when sorting data.
10617  */
10618 Roo.data.SortTypes = {
10619     /**
10620      * Default sort that does nothing
10621      * @param {Mixed} s The value being converted
10622      * @return {Mixed} The comparison value
10623      */
10624     none : function(s){
10625         return s;
10626     },
10627     
10628     /**
10629      * The regular expression used to strip tags
10630      * @type {RegExp}
10631      * @property
10632      */
10633     stripTagsRE : /<\/?[^>]+>/gi,
10634     
10635     /**
10636      * Strips all HTML tags to sort on text only
10637      * @param {Mixed} s The value being converted
10638      * @return {String} The comparison value
10639      */
10640     asText : function(s){
10641         return String(s).replace(this.stripTagsRE, "");
10642     },
10643     
10644     /**
10645      * Strips all HTML tags to sort on text only - Case insensitive
10646      * @param {Mixed} s The value being converted
10647      * @return {String} The comparison value
10648      */
10649     asUCText : function(s){
10650         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10651     },
10652     
10653     /**
10654      * Case insensitive string
10655      * @param {Mixed} s The value being converted
10656      * @return {String} The comparison value
10657      */
10658     asUCString : function(s) {
10659         return String(s).toUpperCase();
10660     },
10661     
10662     /**
10663      * Date sorting
10664      * @param {Mixed} s The value being converted
10665      * @return {Number} The comparison value
10666      */
10667     asDate : function(s) {
10668         if(!s){
10669             return 0;
10670         }
10671         if(s instanceof Date){
10672             return s.getTime();
10673         }
10674         return Date.parse(String(s));
10675     },
10676     
10677     /**
10678      * Float sorting
10679      * @param {Mixed} s The value being converted
10680      * @return {Float} The comparison value
10681      */
10682     asFloat : function(s) {
10683         var val = parseFloat(String(s).replace(/,/g, ""));
10684         if(isNaN(val)) {
10685             val = 0;
10686         }
10687         return val;
10688     },
10689     
10690     /**
10691      * Integer sorting
10692      * @param {Mixed} s The value being converted
10693      * @return {Number} The comparison value
10694      */
10695     asInt : function(s) {
10696         var val = parseInt(String(s).replace(/,/g, ""));
10697         if(isNaN(val)) {
10698             val = 0;
10699         }
10700         return val;
10701     }
10702 };/*
10703  * Based on:
10704  * Ext JS Library 1.1.1
10705  * Copyright(c) 2006-2007, Ext JS, LLC.
10706  *
10707  * Originally Released Under LGPL - original licence link has changed is not relivant.
10708  *
10709  * Fork - LGPL
10710  * <script type="text/javascript">
10711  */
10712
10713 /**
10714 * @class Roo.data.Record
10715  * Instances of this class encapsulate both record <em>definition</em> information, and record
10716  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10717  * to access Records cached in an {@link Roo.data.Store} object.<br>
10718  * <p>
10719  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10720  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10721  * objects.<br>
10722  * <p>
10723  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10724  * @constructor
10725  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10726  * {@link #create}. The parameters are the same.
10727  * @param {Array} data An associative Array of data values keyed by the field name.
10728  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10729  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10730  * not specified an integer id is generated.
10731  */
10732 Roo.data.Record = function(data, id){
10733     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10734     this.data = data;
10735 };
10736
10737 /**
10738  * Generate a constructor for a specific record layout.
10739  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10740  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10741  * Each field definition object may contain the following properties: <ul>
10742  * <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,
10743  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10744  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10745  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10746  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10747  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10748  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10749  * this may be omitted.</p></li>
10750  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10751  * <ul><li>auto (Default, implies no conversion)</li>
10752  * <li>string</li>
10753  * <li>int</li>
10754  * <li>float</li>
10755  * <li>boolean</li>
10756  * <li>date</li></ul></p></li>
10757  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10758  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10759  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10760  * by the Reader into an object that will be stored in the Record. It is passed the
10761  * following parameters:<ul>
10762  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10763  * </ul></p></li>
10764  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10765  * </ul>
10766  * <br>usage:<br><pre><code>
10767 var TopicRecord = Roo.data.Record.create(
10768     {name: 'title', mapping: 'topic_title'},
10769     {name: 'author', mapping: 'username'},
10770     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10771     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10772     {name: 'lastPoster', mapping: 'user2'},
10773     {name: 'excerpt', mapping: 'post_text'}
10774 );
10775
10776 var myNewRecord = new TopicRecord({
10777     title: 'Do my job please',
10778     author: 'noobie',
10779     totalPosts: 1,
10780     lastPost: new Date(),
10781     lastPoster: 'Animal',
10782     excerpt: 'No way dude!'
10783 });
10784 myStore.add(myNewRecord);
10785 </code></pre>
10786  * @method create
10787  * @static
10788  */
10789 Roo.data.Record.create = function(o){
10790     var f = function(){
10791         f.superclass.constructor.apply(this, arguments);
10792     };
10793     Roo.extend(f, Roo.data.Record);
10794     var p = f.prototype;
10795     p.fields = new Roo.util.MixedCollection(false, function(field){
10796         return field.name;
10797     });
10798     for(var i = 0, len = o.length; i < len; i++){
10799         p.fields.add(new Roo.data.Field(o[i]));
10800     }
10801     f.getField = function(name){
10802         return p.fields.get(name);  
10803     };
10804     return f;
10805 };
10806
10807 Roo.data.Record.AUTO_ID = 1000;
10808 Roo.data.Record.EDIT = 'edit';
10809 Roo.data.Record.REJECT = 'reject';
10810 Roo.data.Record.COMMIT = 'commit';
10811
10812 Roo.data.Record.prototype = {
10813     /**
10814      * Readonly flag - true if this record has been modified.
10815      * @type Boolean
10816      */
10817     dirty : false,
10818     editing : false,
10819     error: null,
10820     modified: null,
10821
10822     // private
10823     join : function(store){
10824         this.store = store;
10825     },
10826
10827     /**
10828      * Set the named field to the specified value.
10829      * @param {String} name The name of the field to set.
10830      * @param {Object} value The value to set the field to.
10831      */
10832     set : function(name, value){
10833         if(this.data[name] == value){
10834             return;
10835         }
10836         this.dirty = true;
10837         if(!this.modified){
10838             this.modified = {};
10839         }
10840         if(typeof this.modified[name] == 'undefined'){
10841             this.modified[name] = this.data[name];
10842         }
10843         this.data[name] = value;
10844         if(!this.editing && this.store){
10845             this.store.afterEdit(this);
10846         }       
10847     },
10848
10849     /**
10850      * Get the value of the named field.
10851      * @param {String} name The name of the field to get the value of.
10852      * @return {Object} The value of the field.
10853      */
10854     get : function(name){
10855         return this.data[name]; 
10856     },
10857
10858     // private
10859     beginEdit : function(){
10860         this.editing = true;
10861         this.modified = {}; 
10862     },
10863
10864     // private
10865     cancelEdit : function(){
10866         this.editing = false;
10867         delete this.modified;
10868     },
10869
10870     // private
10871     endEdit : function(){
10872         this.editing = false;
10873         if(this.dirty && this.store){
10874             this.store.afterEdit(this);
10875         }
10876     },
10877
10878     /**
10879      * Usually called by the {@link Roo.data.Store} which owns the Record.
10880      * Rejects all changes made to the Record since either creation, or the last commit operation.
10881      * Modified fields are reverted to their original values.
10882      * <p>
10883      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10884      * of reject operations.
10885      */
10886     reject : function(){
10887         var m = this.modified;
10888         for(var n in m){
10889             if(typeof m[n] != "function"){
10890                 this.data[n] = m[n];
10891             }
10892         }
10893         this.dirty = false;
10894         delete this.modified;
10895         this.editing = false;
10896         if(this.store){
10897             this.store.afterReject(this);
10898         }
10899     },
10900
10901     /**
10902      * Usually called by the {@link Roo.data.Store} which owns the Record.
10903      * Commits all changes made to the Record since either creation, or the last commit operation.
10904      * <p>
10905      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10906      * of commit operations.
10907      */
10908     commit : function(){
10909         this.dirty = false;
10910         delete this.modified;
10911         this.editing = false;
10912         if(this.store){
10913             this.store.afterCommit(this);
10914         }
10915     },
10916
10917     // private
10918     hasError : function(){
10919         return this.error != null;
10920     },
10921
10922     // private
10923     clearError : function(){
10924         this.error = null;
10925     },
10926
10927     /**
10928      * Creates a copy of this record.
10929      * @param {String} id (optional) A new record id if you don't want to use this record's id
10930      * @return {Record}
10931      */
10932     copy : function(newId) {
10933         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10934     }
10935 };/*
10936  * Based on:
10937  * Ext JS Library 1.1.1
10938  * Copyright(c) 2006-2007, Ext JS, LLC.
10939  *
10940  * Originally Released Under LGPL - original licence link has changed is not relivant.
10941  *
10942  * Fork - LGPL
10943  * <script type="text/javascript">
10944  */
10945
10946
10947
10948 /**
10949  * @class Roo.data.Store
10950  * @extends Roo.util.Observable
10951  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10952  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10953  * <p>
10954  * 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
10955  * has no knowledge of the format of the data returned by the Proxy.<br>
10956  * <p>
10957  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10958  * instances from the data object. These records are cached and made available through accessor functions.
10959  * @constructor
10960  * Creates a new Store.
10961  * @param {Object} config A config object containing the objects needed for the Store to access data,
10962  * and read the data into Records.
10963  */
10964 Roo.data.Store = function(config){
10965     this.data = new Roo.util.MixedCollection(false);
10966     this.data.getKey = function(o){
10967         return o.id;
10968     };
10969     this.baseParams = {};
10970     // private
10971     this.paramNames = {
10972         "start" : "start",
10973         "limit" : "limit",
10974         "sort" : "sort",
10975         "dir" : "dir",
10976         "multisort" : "_multisort"
10977     };
10978
10979     if(config && config.data){
10980         this.inlineData = config.data;
10981         delete config.data;
10982     }
10983
10984     Roo.apply(this, config);
10985     
10986     if(this.reader){ // reader passed
10987         this.reader = Roo.factory(this.reader, Roo.data);
10988         this.reader.xmodule = this.xmodule || false;
10989         if(!this.recordType){
10990             this.recordType = this.reader.recordType;
10991         }
10992         if(this.reader.onMetaChange){
10993             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10994         }
10995     }
10996
10997     if(this.recordType){
10998         this.fields = this.recordType.prototype.fields;
10999     }
11000     this.modified = [];
11001
11002     this.addEvents({
11003         /**
11004          * @event datachanged
11005          * Fires when the data cache has changed, and a widget which is using this Store
11006          * as a Record cache should refresh its view.
11007          * @param {Store} this
11008          */
11009         datachanged : true,
11010         /**
11011          * @event metachange
11012          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11013          * @param {Store} this
11014          * @param {Object} meta The JSON metadata
11015          */
11016         metachange : true,
11017         /**
11018          * @event add
11019          * Fires when Records have been added to the Store
11020          * @param {Store} this
11021          * @param {Roo.data.Record[]} records The array of Records added
11022          * @param {Number} index The index at which the record(s) were added
11023          */
11024         add : true,
11025         /**
11026          * @event remove
11027          * Fires when a Record has been removed from the Store
11028          * @param {Store} this
11029          * @param {Roo.data.Record} record The Record that was removed
11030          * @param {Number} index The index at which the record was removed
11031          */
11032         remove : true,
11033         /**
11034          * @event update
11035          * Fires when a Record has been updated
11036          * @param {Store} this
11037          * @param {Roo.data.Record} record The Record that was updated
11038          * @param {String} operation The update operation being performed.  Value may be one of:
11039          * <pre><code>
11040  Roo.data.Record.EDIT
11041  Roo.data.Record.REJECT
11042  Roo.data.Record.COMMIT
11043          * </code></pre>
11044          */
11045         update : true,
11046         /**
11047          * @event clear
11048          * Fires when the data cache has been cleared.
11049          * @param {Store} this
11050          */
11051         clear : true,
11052         /**
11053          * @event beforeload
11054          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11055          * the load action will be canceled.
11056          * @param {Store} this
11057          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11058          */
11059         beforeload : true,
11060         /**
11061          * @event beforeloadadd
11062          * Fires after a new set of Records has been loaded.
11063          * @param {Store} this
11064          * @param {Roo.data.Record[]} records The Records that were loaded
11065          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11066          */
11067         beforeloadadd : true,
11068         /**
11069          * @event load
11070          * Fires after a new set of Records has been loaded, before they are added to the store.
11071          * @param {Store} this
11072          * @param {Roo.data.Record[]} records The Records that were loaded
11073          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11074          * @params {Object} return from reader
11075          */
11076         load : true,
11077         /**
11078          * @event loadexception
11079          * Fires if an exception occurs in the Proxy during loading.
11080          * Called with the signature of the Proxy's "loadexception" event.
11081          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11082          * 
11083          * @param {Proxy} 
11084          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11085          * @param {Object} load options 
11086          * @param {Object} jsonData from your request (normally this contains the Exception)
11087          */
11088         loadexception : true
11089     });
11090     
11091     if(this.proxy){
11092         this.proxy = Roo.factory(this.proxy, Roo.data);
11093         this.proxy.xmodule = this.xmodule || false;
11094         this.relayEvents(this.proxy,  ["loadexception"]);
11095     }
11096     this.sortToggle = {};
11097     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11098
11099     Roo.data.Store.superclass.constructor.call(this);
11100
11101     if(this.inlineData){
11102         this.loadData(this.inlineData);
11103         delete this.inlineData;
11104     }
11105 };
11106
11107 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11108      /**
11109     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11110     * without a remote query - used by combo/forms at present.
11111     */
11112     
11113     /**
11114     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11115     */
11116     /**
11117     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11118     */
11119     /**
11120     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11121     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11122     */
11123     /**
11124     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11125     * on any HTTP request
11126     */
11127     /**
11128     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11129     */
11130     /**
11131     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11132     */
11133     multiSort: false,
11134     /**
11135     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11136     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11137     */
11138     remoteSort : false,
11139
11140     /**
11141     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11142      * loaded or when a record is removed. (defaults to false).
11143     */
11144     pruneModifiedRecords : false,
11145
11146     // private
11147     lastOptions : null,
11148
11149     /**
11150      * Add Records to the Store and fires the add event.
11151      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11152      */
11153     add : function(records){
11154         records = [].concat(records);
11155         for(var i = 0, len = records.length; i < len; i++){
11156             records[i].join(this);
11157         }
11158         var index = this.data.length;
11159         this.data.addAll(records);
11160         this.fireEvent("add", this, records, index);
11161     },
11162
11163     /**
11164      * Remove a Record from the Store and fires the remove event.
11165      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11166      */
11167     remove : function(record){
11168         var index = this.data.indexOf(record);
11169         this.data.removeAt(index);
11170  
11171         if(this.pruneModifiedRecords){
11172             this.modified.remove(record);
11173         }
11174         this.fireEvent("remove", this, record, index);
11175     },
11176
11177     /**
11178      * Remove all Records from the Store and fires the clear event.
11179      */
11180     removeAll : function(){
11181         this.data.clear();
11182         if(this.pruneModifiedRecords){
11183             this.modified = [];
11184         }
11185         this.fireEvent("clear", this);
11186     },
11187
11188     /**
11189      * Inserts Records to the Store at the given index and fires the add event.
11190      * @param {Number} index The start index at which to insert the passed Records.
11191      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11192      */
11193     insert : function(index, records){
11194         records = [].concat(records);
11195         for(var i = 0, len = records.length; i < len; i++){
11196             this.data.insert(index, records[i]);
11197             records[i].join(this);
11198         }
11199         this.fireEvent("add", this, records, index);
11200     },
11201
11202     /**
11203      * Get the index within the cache of the passed Record.
11204      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11205      * @return {Number} The index of the passed Record. Returns -1 if not found.
11206      */
11207     indexOf : function(record){
11208         return this.data.indexOf(record);
11209     },
11210
11211     /**
11212      * Get the index within the cache of the Record with the passed id.
11213      * @param {String} id The id of the Record to find.
11214      * @return {Number} The index of the Record. Returns -1 if not found.
11215      */
11216     indexOfId : function(id){
11217         return this.data.indexOfKey(id);
11218     },
11219
11220     /**
11221      * Get the Record with the specified id.
11222      * @param {String} id The id of the Record to find.
11223      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11224      */
11225     getById : function(id){
11226         return this.data.key(id);
11227     },
11228
11229     /**
11230      * Get the Record at the specified index.
11231      * @param {Number} index The index of the Record to find.
11232      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11233      */
11234     getAt : function(index){
11235         return this.data.itemAt(index);
11236     },
11237
11238     /**
11239      * Returns a range of Records between specified indices.
11240      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11241      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11242      * @return {Roo.data.Record[]} An array of Records
11243      */
11244     getRange : function(start, end){
11245         return this.data.getRange(start, end);
11246     },
11247
11248     // private
11249     storeOptions : function(o){
11250         o = Roo.apply({}, o);
11251         delete o.callback;
11252         delete o.scope;
11253         this.lastOptions = o;
11254     },
11255
11256     /**
11257      * Loads the Record cache from the configured Proxy using the configured Reader.
11258      * <p>
11259      * If using remote paging, then the first load call must specify the <em>start</em>
11260      * and <em>limit</em> properties in the options.params property to establish the initial
11261      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11262      * <p>
11263      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11264      * and this call will return before the new data has been loaded. Perform any post-processing
11265      * in a callback function, or in a "load" event handler.</strong>
11266      * <p>
11267      * @param {Object} options An object containing properties which control loading options:<ul>
11268      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11269      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11270      * passed the following arguments:<ul>
11271      * <li>r : Roo.data.Record[]</li>
11272      * <li>options: Options object from the load call</li>
11273      * <li>success: Boolean success indicator</li></ul></li>
11274      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11275      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11276      * </ul>
11277      */
11278     load : function(options){
11279         options = options || {};
11280         if(this.fireEvent("beforeload", this, options) !== false){
11281             this.storeOptions(options);
11282             var p = Roo.apply(options.params || {}, this.baseParams);
11283             // if meta was not loaded from remote source.. try requesting it.
11284             if (!this.reader.metaFromRemote) {
11285                 p._requestMeta = 1;
11286             }
11287             if(this.sortInfo && this.remoteSort){
11288                 var pn = this.paramNames;
11289                 p[pn["sort"]] = this.sortInfo.field;
11290                 p[pn["dir"]] = this.sortInfo.direction;
11291             }
11292             if (this.multiSort) {
11293                 var pn = this.paramNames;
11294                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11295             }
11296             
11297             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11298         }
11299     },
11300
11301     /**
11302      * Reloads the Record cache from the configured Proxy using the configured Reader and
11303      * the options from the last load operation performed.
11304      * @param {Object} options (optional) An object containing properties which may override the options
11305      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11306      * the most recently used options are reused).
11307      */
11308     reload : function(options){
11309         this.load(Roo.applyIf(options||{}, this.lastOptions));
11310     },
11311
11312     // private
11313     // Called as a callback by the Reader during a load operation.
11314     loadRecords : function(o, options, success){
11315         if(!o || success === false){
11316             if(success !== false){
11317                 this.fireEvent("load", this, [], options, o);
11318             }
11319             if(options.callback){
11320                 options.callback.call(options.scope || this, [], options, false);
11321             }
11322             return;
11323         }
11324         // if data returned failure - throw an exception.
11325         if (o.success === false) {
11326             // show a message if no listener is registered.
11327             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11328                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11329             }
11330             // loadmask wil be hooked into this..
11331             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11332             return;
11333         }
11334         var r = o.records, t = o.totalRecords || r.length;
11335         
11336         this.fireEvent("beforeloadadd", this, r, options, o);
11337         
11338         if(!options || options.add !== true){
11339             if(this.pruneModifiedRecords){
11340                 this.modified = [];
11341             }
11342             for(var i = 0, len = r.length; i < len; i++){
11343                 r[i].join(this);
11344             }
11345             if(this.snapshot){
11346                 this.data = this.snapshot;
11347                 delete this.snapshot;
11348             }
11349             this.data.clear();
11350             this.data.addAll(r);
11351             this.totalLength = t;
11352             this.applySort();
11353             this.fireEvent("datachanged", this);
11354         }else{
11355             this.totalLength = Math.max(t, this.data.length+r.length);
11356             this.add(r);
11357         }
11358         
11359         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11360                 
11361             var e = new Roo.data.Record({});
11362
11363             e.set(this.parent.displayField, this.parent.emptyTitle);
11364             e.set(this.parent.valueField, '');
11365
11366             this.insert(0, e);
11367         }
11368             
11369         this.fireEvent("load", this, r, options, o);
11370         if(options.callback){
11371             options.callback.call(options.scope || this, r, options, true);
11372         }
11373     },
11374
11375
11376     /**
11377      * Loads data from a passed data block. A Reader which understands the format of the data
11378      * must have been configured in the constructor.
11379      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11380      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11381      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11382      */
11383     loadData : function(o, append){
11384         var r = this.reader.readRecords(o);
11385         this.loadRecords(r, {add: append}, true);
11386     },
11387
11388     /**
11389      * Gets the number of cached records.
11390      * <p>
11391      * <em>If using paging, this may not be the total size of the dataset. If the data object
11392      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11393      * the data set size</em>
11394      */
11395     getCount : function(){
11396         return this.data.length || 0;
11397     },
11398
11399     /**
11400      * Gets the total number of records in the dataset as returned by the server.
11401      * <p>
11402      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11403      * the dataset size</em>
11404      */
11405     getTotalCount : function(){
11406         return this.totalLength || 0;
11407     },
11408
11409     /**
11410      * Returns the sort state of the Store as an object with two properties:
11411      * <pre><code>
11412  field {String} The name of the field by which the Records are sorted
11413  direction {String} The sort order, "ASC" or "DESC"
11414      * </code></pre>
11415      */
11416     getSortState : function(){
11417         return this.sortInfo;
11418     },
11419
11420     // private
11421     applySort : function(){
11422         if(this.sortInfo && !this.remoteSort){
11423             var s = this.sortInfo, f = s.field;
11424             var st = this.fields.get(f).sortType;
11425             var fn = function(r1, r2){
11426                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11427                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11428             };
11429             this.data.sort(s.direction, fn);
11430             if(this.snapshot && this.snapshot != this.data){
11431                 this.snapshot.sort(s.direction, fn);
11432             }
11433         }
11434     },
11435
11436     /**
11437      * Sets the default sort column and order to be used by the next load operation.
11438      * @param {String} fieldName The name of the field to sort by.
11439      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11440      */
11441     setDefaultSort : function(field, dir){
11442         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11443     },
11444
11445     /**
11446      * Sort the Records.
11447      * If remote sorting is used, the sort is performed on the server, and the cache is
11448      * reloaded. If local sorting is used, the cache is sorted internally.
11449      * @param {String} fieldName The name of the field to sort by.
11450      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11451      */
11452     sort : function(fieldName, dir){
11453         var f = this.fields.get(fieldName);
11454         if(!dir){
11455             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11456             
11457             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11458                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11459             }else{
11460                 dir = f.sortDir;
11461             }
11462         }
11463         this.sortToggle[f.name] = dir;
11464         this.sortInfo = {field: f.name, direction: dir};
11465         if(!this.remoteSort){
11466             this.applySort();
11467             this.fireEvent("datachanged", this);
11468         }else{
11469             this.load(this.lastOptions);
11470         }
11471     },
11472
11473     /**
11474      * Calls the specified function for each of the Records in the cache.
11475      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11476      * Returning <em>false</em> aborts and exits the iteration.
11477      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11478      */
11479     each : function(fn, scope){
11480         this.data.each(fn, scope);
11481     },
11482
11483     /**
11484      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11485      * (e.g., during paging).
11486      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11487      */
11488     getModifiedRecords : function(){
11489         return this.modified;
11490     },
11491
11492     // private
11493     createFilterFn : function(property, value, anyMatch){
11494         if(!value.exec){ // not a regex
11495             value = String(value);
11496             if(value.length == 0){
11497                 return false;
11498             }
11499             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11500         }
11501         return function(r){
11502             return value.test(r.data[property]);
11503         };
11504     },
11505
11506     /**
11507      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11508      * @param {String} property A field on your records
11509      * @param {Number} start The record index to start at (defaults to 0)
11510      * @param {Number} end The last record index to include (defaults to length - 1)
11511      * @return {Number} The sum
11512      */
11513     sum : function(property, start, end){
11514         var rs = this.data.items, v = 0;
11515         start = start || 0;
11516         end = (end || end === 0) ? end : rs.length-1;
11517
11518         for(var i = start; i <= end; i++){
11519             v += (rs[i].data[property] || 0);
11520         }
11521         return v;
11522     },
11523
11524     /**
11525      * Filter the records by a specified property.
11526      * @param {String} field A field on your records
11527      * @param {String/RegExp} value Either a string that the field
11528      * should start with or a RegExp to test against the field
11529      * @param {Boolean} anyMatch True to match any part not just the beginning
11530      */
11531     filter : function(property, value, anyMatch){
11532         var fn = this.createFilterFn(property, value, anyMatch);
11533         return fn ? this.filterBy(fn) : this.clearFilter();
11534     },
11535
11536     /**
11537      * Filter by a function. The specified function will be called with each
11538      * record in this data source. If the function returns true the record is included,
11539      * otherwise it is filtered.
11540      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11541      * @param {Object} scope (optional) The scope of the function (defaults to this)
11542      */
11543     filterBy : function(fn, scope){
11544         this.snapshot = this.snapshot || this.data;
11545         this.data = this.queryBy(fn, scope||this);
11546         this.fireEvent("datachanged", this);
11547     },
11548
11549     /**
11550      * Query 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      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11556      */
11557     query : function(property, value, anyMatch){
11558         var fn = this.createFilterFn(property, value, anyMatch);
11559         return fn ? this.queryBy(fn) : this.data.clone();
11560     },
11561
11562     /**
11563      * Query by a function. The specified function will be called with each
11564      * record in this data source. If the function returns true the record is included
11565      * in the results.
11566      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11567      * @param {Object} scope (optional) The scope of the function (defaults to this)
11568       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11569      **/
11570     queryBy : function(fn, scope){
11571         var data = this.snapshot || this.data;
11572         return data.filterBy(fn, scope||this);
11573     },
11574
11575     /**
11576      * Collects unique values for a particular dataIndex from this store.
11577      * @param {String} dataIndex The property to collect
11578      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11579      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11580      * @return {Array} An array of the unique values
11581      **/
11582     collect : function(dataIndex, allowNull, bypassFilter){
11583         var d = (bypassFilter === true && this.snapshot) ?
11584                 this.snapshot.items : this.data.items;
11585         var v, sv, r = [], l = {};
11586         for(var i = 0, len = d.length; i < len; i++){
11587             v = d[i].data[dataIndex];
11588             sv = String(v);
11589             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11590                 l[sv] = true;
11591                 r[r.length] = v;
11592             }
11593         }
11594         return r;
11595     },
11596
11597     /**
11598      * Revert to a view of the Record cache with no filtering applied.
11599      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11600      */
11601     clearFilter : function(suppressEvent){
11602         if(this.snapshot && this.snapshot != this.data){
11603             this.data = this.snapshot;
11604             delete this.snapshot;
11605             if(suppressEvent !== true){
11606                 this.fireEvent("datachanged", this);
11607             }
11608         }
11609     },
11610
11611     // private
11612     afterEdit : function(record){
11613         if(this.modified.indexOf(record) == -1){
11614             this.modified.push(record);
11615         }
11616         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11617     },
11618     
11619     // private
11620     afterReject : function(record){
11621         this.modified.remove(record);
11622         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11623     },
11624
11625     // private
11626     afterCommit : function(record){
11627         this.modified.remove(record);
11628         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11629     },
11630
11631     /**
11632      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11633      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11634      */
11635     commitChanges : function(){
11636         var m = this.modified.slice(0);
11637         this.modified = [];
11638         for(var i = 0, len = m.length; i < len; i++){
11639             m[i].commit();
11640         }
11641     },
11642
11643     /**
11644      * Cancel outstanding changes on all changed records.
11645      */
11646     rejectChanges : function(){
11647         var m = this.modified.slice(0);
11648         this.modified = [];
11649         for(var i = 0, len = m.length; i < len; i++){
11650             m[i].reject();
11651         }
11652     },
11653
11654     onMetaChange : function(meta, rtype, o){
11655         this.recordType = rtype;
11656         this.fields = rtype.prototype.fields;
11657         delete this.snapshot;
11658         this.sortInfo = meta.sortInfo || this.sortInfo;
11659         this.modified = [];
11660         this.fireEvent('metachange', this, this.reader.meta);
11661     },
11662     
11663     moveIndex : function(data, type)
11664     {
11665         var index = this.indexOf(data);
11666         
11667         var newIndex = index + type;
11668         
11669         this.remove(data);
11670         
11671         this.insert(newIndex, data);
11672         
11673     }
11674 });/*
11675  * Based on:
11676  * Ext JS Library 1.1.1
11677  * Copyright(c) 2006-2007, Ext JS, LLC.
11678  *
11679  * Originally Released Under LGPL - original licence link has changed is not relivant.
11680  *
11681  * Fork - LGPL
11682  * <script type="text/javascript">
11683  */
11684
11685 /**
11686  * @class Roo.data.SimpleStore
11687  * @extends Roo.data.Store
11688  * Small helper class to make creating Stores from Array data easier.
11689  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11690  * @cfg {Array} fields An array of field definition objects, or field name strings.
11691  * @cfg {Array} data The multi-dimensional array of data
11692  * @constructor
11693  * @param {Object} config
11694  */
11695 Roo.data.SimpleStore = function(config){
11696     Roo.data.SimpleStore.superclass.constructor.call(this, {
11697         isLocal : true,
11698         reader: new Roo.data.ArrayReader({
11699                 id: config.id
11700             },
11701             Roo.data.Record.create(config.fields)
11702         ),
11703         proxy : new Roo.data.MemoryProxy(config.data)
11704     });
11705     this.load();
11706 };
11707 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11708  * Based on:
11709  * Ext JS Library 1.1.1
11710  * Copyright(c) 2006-2007, Ext JS, LLC.
11711  *
11712  * Originally Released Under LGPL - original licence link has changed is not relivant.
11713  *
11714  * Fork - LGPL
11715  * <script type="text/javascript">
11716  */
11717
11718 /**
11719 /**
11720  * @extends Roo.data.Store
11721  * @class Roo.data.JsonStore
11722  * Small helper class to make creating Stores for JSON data easier. <br/>
11723 <pre><code>
11724 var store = new Roo.data.JsonStore({
11725     url: 'get-images.php',
11726     root: 'images',
11727     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11728 });
11729 </code></pre>
11730  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11731  * JsonReader and HttpProxy (unless inline data is provided).</b>
11732  * @cfg {Array} fields An array of field definition objects, or field name strings.
11733  * @constructor
11734  * @param {Object} config
11735  */
11736 Roo.data.JsonStore = function(c){
11737     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11738         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11739         reader: new Roo.data.JsonReader(c, c.fields)
11740     }));
11741 };
11742 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11743  * Based on:
11744  * Ext JS Library 1.1.1
11745  * Copyright(c) 2006-2007, Ext JS, LLC.
11746  *
11747  * Originally Released Under LGPL - original licence link has changed is not relivant.
11748  *
11749  * Fork - LGPL
11750  * <script type="text/javascript">
11751  */
11752
11753  
11754 Roo.data.Field = function(config){
11755     if(typeof config == "string"){
11756         config = {name: config};
11757     }
11758     Roo.apply(this, config);
11759     
11760     if(!this.type){
11761         this.type = "auto";
11762     }
11763     
11764     var st = Roo.data.SortTypes;
11765     // named sortTypes are supported, here we look them up
11766     if(typeof this.sortType == "string"){
11767         this.sortType = st[this.sortType];
11768     }
11769     
11770     // set default sortType for strings and dates
11771     if(!this.sortType){
11772         switch(this.type){
11773             case "string":
11774                 this.sortType = st.asUCString;
11775                 break;
11776             case "date":
11777                 this.sortType = st.asDate;
11778                 break;
11779             default:
11780                 this.sortType = st.none;
11781         }
11782     }
11783
11784     // define once
11785     var stripRe = /[\$,%]/g;
11786
11787     // prebuilt conversion function for this field, instead of
11788     // switching every time we're reading a value
11789     if(!this.convert){
11790         var cv, dateFormat = this.dateFormat;
11791         switch(this.type){
11792             case "":
11793             case "auto":
11794             case undefined:
11795                 cv = function(v){ return v; };
11796                 break;
11797             case "string":
11798                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11799                 break;
11800             case "int":
11801                 cv = function(v){
11802                     return v !== undefined && v !== null && v !== '' ?
11803                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11804                     };
11805                 break;
11806             case "float":
11807                 cv = function(v){
11808                     return v !== undefined && v !== null && v !== '' ?
11809                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11810                     };
11811                 break;
11812             case "bool":
11813             case "boolean":
11814                 cv = function(v){ return v === true || v === "true" || v == 1; };
11815                 break;
11816             case "date":
11817                 cv = function(v){
11818                     if(!v){
11819                         return '';
11820                     }
11821                     if(v instanceof Date){
11822                         return v;
11823                     }
11824                     if(dateFormat){
11825                         if(dateFormat == "timestamp"){
11826                             return new Date(v*1000);
11827                         }
11828                         return Date.parseDate(v, dateFormat);
11829                     }
11830                     var parsed = Date.parse(v);
11831                     return parsed ? new Date(parsed) : null;
11832                 };
11833              break;
11834             
11835         }
11836         this.convert = cv;
11837     }
11838 };
11839
11840 Roo.data.Field.prototype = {
11841     dateFormat: null,
11842     defaultValue: "",
11843     mapping: null,
11844     sortType : null,
11845     sortDir : "ASC"
11846 };/*
11847  * Based on:
11848  * Ext JS Library 1.1.1
11849  * Copyright(c) 2006-2007, Ext JS, LLC.
11850  *
11851  * Originally Released Under LGPL - original licence link has changed is not relivant.
11852  *
11853  * Fork - LGPL
11854  * <script type="text/javascript">
11855  */
11856  
11857 // Base class for reading structured data from a data source.  This class is intended to be
11858 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11859
11860 /**
11861  * @class Roo.data.DataReader
11862  * Base class for reading structured data from a data source.  This class is intended to be
11863  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11864  */
11865
11866 Roo.data.DataReader = function(meta, recordType){
11867     
11868     this.meta = meta;
11869     
11870     this.recordType = recordType instanceof Array ? 
11871         Roo.data.Record.create(recordType) : recordType;
11872 };
11873
11874 Roo.data.DataReader.prototype = {
11875      /**
11876      * Create an empty record
11877      * @param {Object} data (optional) - overlay some values
11878      * @return {Roo.data.Record} record created.
11879      */
11880     newRow :  function(d) {
11881         var da =  {};
11882         this.recordType.prototype.fields.each(function(c) {
11883             switch( c.type) {
11884                 case 'int' : da[c.name] = 0; break;
11885                 case 'date' : da[c.name] = new Date(); break;
11886                 case 'float' : da[c.name] = 0.0; break;
11887                 case 'boolean' : da[c.name] = false; break;
11888                 default : da[c.name] = ""; break;
11889             }
11890             
11891         });
11892         return new this.recordType(Roo.apply(da, d));
11893     }
11894     
11895 };/*
11896  * Based on:
11897  * Ext JS Library 1.1.1
11898  * Copyright(c) 2006-2007, Ext JS, LLC.
11899  *
11900  * Originally Released Under LGPL - original licence link has changed is not relivant.
11901  *
11902  * Fork - LGPL
11903  * <script type="text/javascript">
11904  */
11905
11906 /**
11907  * @class Roo.data.DataProxy
11908  * @extends Roo.data.Observable
11909  * This class is an abstract base class for implementations which provide retrieval of
11910  * unformatted data objects.<br>
11911  * <p>
11912  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11913  * (of the appropriate type which knows how to parse the data object) to provide a block of
11914  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11915  * <p>
11916  * Custom implementations must implement the load method as described in
11917  * {@link Roo.data.HttpProxy#load}.
11918  */
11919 Roo.data.DataProxy = function(){
11920     this.addEvents({
11921         /**
11922          * @event beforeload
11923          * Fires before a network request is made to retrieve a data object.
11924          * @param {Object} This DataProxy object.
11925          * @param {Object} params The params parameter to the load function.
11926          */
11927         beforeload : true,
11928         /**
11929          * @event load
11930          * Fires before the load method's callback is called.
11931          * @param {Object} This DataProxy object.
11932          * @param {Object} o The data object.
11933          * @param {Object} arg The callback argument object passed to the load function.
11934          */
11935         load : true,
11936         /**
11937          * @event loadexception
11938          * Fires if an Exception occurs during data retrieval.
11939          * @param {Object} This DataProxy object.
11940          * @param {Object} o The data object.
11941          * @param {Object} arg The callback argument object passed to the load function.
11942          * @param {Object} e The Exception.
11943          */
11944         loadexception : true
11945     });
11946     Roo.data.DataProxy.superclass.constructor.call(this);
11947 };
11948
11949 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11950
11951     /**
11952      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11953      */
11954 /*
11955  * Based on:
11956  * Ext JS Library 1.1.1
11957  * Copyright(c) 2006-2007, Ext JS, LLC.
11958  *
11959  * Originally Released Under LGPL - original licence link has changed is not relivant.
11960  *
11961  * Fork - LGPL
11962  * <script type="text/javascript">
11963  */
11964 /**
11965  * @class Roo.data.MemoryProxy
11966  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11967  * to the Reader when its load method is called.
11968  * @constructor
11969  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11970  */
11971 Roo.data.MemoryProxy = function(data){
11972     if (data.data) {
11973         data = data.data;
11974     }
11975     Roo.data.MemoryProxy.superclass.constructor.call(this);
11976     this.data = data;
11977 };
11978
11979 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11980     
11981     /**
11982      * Load data from the requested source (in this case an in-memory
11983      * data object passed to the constructor), read the data object into
11984      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11985      * process that block using the passed callback.
11986      * @param {Object} params This parameter is not used by the MemoryProxy class.
11987      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11988      * object into a block of Roo.data.Records.
11989      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11990      * The function must be passed <ul>
11991      * <li>The Record block object</li>
11992      * <li>The "arg" argument from the load function</li>
11993      * <li>A boolean success indicator</li>
11994      * </ul>
11995      * @param {Object} scope The scope in which to call the callback
11996      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11997      */
11998     load : function(params, reader, callback, scope, arg){
11999         params = params || {};
12000         var result;
12001         try {
12002             result = reader.readRecords(this.data);
12003         }catch(e){
12004             this.fireEvent("loadexception", this, arg, null, e);
12005             callback.call(scope, null, arg, false);
12006             return;
12007         }
12008         callback.call(scope, result, arg, true);
12009     },
12010     
12011     // private
12012     update : function(params, records){
12013         
12014     }
12015 });/*
12016  * Based on:
12017  * Ext JS Library 1.1.1
12018  * Copyright(c) 2006-2007, Ext JS, LLC.
12019  *
12020  * Originally Released Under LGPL - original licence link has changed is not relivant.
12021  *
12022  * Fork - LGPL
12023  * <script type="text/javascript">
12024  */
12025 /**
12026  * @class Roo.data.HttpProxy
12027  * @extends Roo.data.DataProxy
12028  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12029  * configured to reference a certain URL.<br><br>
12030  * <p>
12031  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12032  * from which the running page was served.<br><br>
12033  * <p>
12034  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12035  * <p>
12036  * Be aware that to enable the browser to parse an XML document, the server must set
12037  * the Content-Type header in the HTTP response to "text/xml".
12038  * @constructor
12039  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12040  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12041  * will be used to make the request.
12042  */
12043 Roo.data.HttpProxy = function(conn){
12044     Roo.data.HttpProxy.superclass.constructor.call(this);
12045     // is conn a conn config or a real conn?
12046     this.conn = conn;
12047     this.useAjax = !conn || !conn.events;
12048   
12049 };
12050
12051 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12052     // thse are take from connection...
12053     
12054     /**
12055      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12056      */
12057     /**
12058      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12059      * extra parameters to each request made by this object. (defaults to undefined)
12060      */
12061     /**
12062      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12063      *  to each request made by this object. (defaults to undefined)
12064      */
12065     /**
12066      * @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)
12067      */
12068     /**
12069      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12070      */
12071      /**
12072      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12073      * @type Boolean
12074      */
12075   
12076
12077     /**
12078      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12079      * @type Boolean
12080      */
12081     /**
12082      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12083      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12084      * a finer-grained basis than the DataProxy events.
12085      */
12086     getConnection : function(){
12087         return this.useAjax ? Roo.Ajax : this.conn;
12088     },
12089
12090     /**
12091      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12092      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12093      * process that block using the passed callback.
12094      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12095      * for the request to the remote server.
12096      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12097      * object into a block of Roo.data.Records.
12098      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12099      * The function must be passed <ul>
12100      * <li>The Record block object</li>
12101      * <li>The "arg" argument from the load function</li>
12102      * <li>A boolean success indicator</li>
12103      * </ul>
12104      * @param {Object} scope The scope in which to call the callback
12105      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12106      */
12107     load : function(params, reader, callback, scope, arg){
12108         if(this.fireEvent("beforeload", this, params) !== false){
12109             var  o = {
12110                 params : params || {},
12111                 request: {
12112                     callback : callback,
12113                     scope : scope,
12114                     arg : arg
12115                 },
12116                 reader: reader,
12117                 callback : this.loadResponse,
12118                 scope: this
12119             };
12120             if(this.useAjax){
12121                 Roo.applyIf(o, this.conn);
12122                 if(this.activeRequest){
12123                     Roo.Ajax.abort(this.activeRequest);
12124                 }
12125                 this.activeRequest = Roo.Ajax.request(o);
12126             }else{
12127                 this.conn.request(o);
12128             }
12129         }else{
12130             callback.call(scope||this, null, arg, false);
12131         }
12132     },
12133
12134     // private
12135     loadResponse : function(o, success, response){
12136         delete this.activeRequest;
12137         if(!success){
12138             this.fireEvent("loadexception", this, o, response);
12139             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12140             return;
12141         }
12142         var result;
12143         try {
12144             result = o.reader.read(response);
12145         }catch(e){
12146             this.fireEvent("loadexception", this, o, response, e);
12147             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12148             return;
12149         }
12150         
12151         this.fireEvent("load", this, o, o.request.arg);
12152         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12153     },
12154
12155     // private
12156     update : function(dataSet){
12157
12158     },
12159
12160     // private
12161     updateResponse : function(dataSet){
12162
12163     }
12164 });/*
12165  * Based on:
12166  * Ext JS Library 1.1.1
12167  * Copyright(c) 2006-2007, Ext JS, LLC.
12168  *
12169  * Originally Released Under LGPL - original licence link has changed is not relivant.
12170  *
12171  * Fork - LGPL
12172  * <script type="text/javascript">
12173  */
12174
12175 /**
12176  * @class Roo.data.ScriptTagProxy
12177  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12178  * other than the originating domain of the running page.<br><br>
12179  * <p>
12180  * <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
12181  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12182  * <p>
12183  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12184  * source code that is used as the source inside a &lt;script> tag.<br><br>
12185  * <p>
12186  * In order for the browser to process the returned data, the server must wrap the data object
12187  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12188  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12189  * depending on whether the callback name was passed:
12190  * <p>
12191  * <pre><code>
12192 boolean scriptTag = false;
12193 String cb = request.getParameter("callback");
12194 if (cb != null) {
12195     scriptTag = true;
12196     response.setContentType("text/javascript");
12197 } else {
12198     response.setContentType("application/x-json");
12199 }
12200 Writer out = response.getWriter();
12201 if (scriptTag) {
12202     out.write(cb + "(");
12203 }
12204 out.print(dataBlock.toJsonString());
12205 if (scriptTag) {
12206     out.write(");");
12207 }
12208 </pre></code>
12209  *
12210  * @constructor
12211  * @param {Object} config A configuration object.
12212  */
12213 Roo.data.ScriptTagProxy = function(config){
12214     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12215     Roo.apply(this, config);
12216     this.head = document.getElementsByTagName("head")[0];
12217 };
12218
12219 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12220
12221 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12222     /**
12223      * @cfg {String} url The URL from which to request the data object.
12224      */
12225     /**
12226      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12227      */
12228     timeout : 30000,
12229     /**
12230      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12231      * the server the name of the callback function set up by the load call to process the returned data object.
12232      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12233      * javascript output which calls this named function passing the data object as its only parameter.
12234      */
12235     callbackParam : "callback",
12236     /**
12237      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12238      * name to the request.
12239      */
12240     nocache : true,
12241
12242     /**
12243      * Load data from the configured URL, read the data object into
12244      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12245      * process that block using the passed callback.
12246      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12247      * for the request to the remote server.
12248      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12249      * object into a block of Roo.data.Records.
12250      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12251      * The function must be passed <ul>
12252      * <li>The Record block object</li>
12253      * <li>The "arg" argument from the load function</li>
12254      * <li>A boolean success indicator</li>
12255      * </ul>
12256      * @param {Object} scope The scope in which to call the callback
12257      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12258      */
12259     load : function(params, reader, callback, scope, arg){
12260         if(this.fireEvent("beforeload", this, params) !== false){
12261
12262             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12263
12264             var url = this.url;
12265             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12266             if(this.nocache){
12267                 url += "&_dc=" + (new Date().getTime());
12268             }
12269             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12270             var trans = {
12271                 id : transId,
12272                 cb : "stcCallback"+transId,
12273                 scriptId : "stcScript"+transId,
12274                 params : params,
12275                 arg : arg,
12276                 url : url,
12277                 callback : callback,
12278                 scope : scope,
12279                 reader : reader
12280             };
12281             var conn = this;
12282
12283             window[trans.cb] = function(o){
12284                 conn.handleResponse(o, trans);
12285             };
12286
12287             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12288
12289             if(this.autoAbort !== false){
12290                 this.abort();
12291             }
12292
12293             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12294
12295             var script = document.createElement("script");
12296             script.setAttribute("src", url);
12297             script.setAttribute("type", "text/javascript");
12298             script.setAttribute("id", trans.scriptId);
12299             this.head.appendChild(script);
12300
12301             this.trans = trans;
12302         }else{
12303             callback.call(scope||this, null, arg, false);
12304         }
12305     },
12306
12307     // private
12308     isLoading : function(){
12309         return this.trans ? true : false;
12310     },
12311
12312     /**
12313      * Abort the current server request.
12314      */
12315     abort : function(){
12316         if(this.isLoading()){
12317             this.destroyTrans(this.trans);
12318         }
12319     },
12320
12321     // private
12322     destroyTrans : function(trans, isLoaded){
12323         this.head.removeChild(document.getElementById(trans.scriptId));
12324         clearTimeout(trans.timeoutId);
12325         if(isLoaded){
12326             window[trans.cb] = undefined;
12327             try{
12328                 delete window[trans.cb];
12329             }catch(e){}
12330         }else{
12331             // if hasn't been loaded, wait for load to remove it to prevent script error
12332             window[trans.cb] = function(){
12333                 window[trans.cb] = undefined;
12334                 try{
12335                     delete window[trans.cb];
12336                 }catch(e){}
12337             };
12338         }
12339     },
12340
12341     // private
12342     handleResponse : function(o, trans){
12343         this.trans = false;
12344         this.destroyTrans(trans, true);
12345         var result;
12346         try {
12347             result = trans.reader.readRecords(o);
12348         }catch(e){
12349             this.fireEvent("loadexception", this, o, trans.arg, e);
12350             trans.callback.call(trans.scope||window, null, trans.arg, false);
12351             return;
12352         }
12353         this.fireEvent("load", this, o, trans.arg);
12354         trans.callback.call(trans.scope||window, result, trans.arg, true);
12355     },
12356
12357     // private
12358     handleFailure : function(trans){
12359         this.trans = false;
12360         this.destroyTrans(trans, false);
12361         this.fireEvent("loadexception", this, null, trans.arg);
12362         trans.callback.call(trans.scope||window, null, trans.arg, false);
12363     }
12364 });/*
12365  * Based on:
12366  * Ext JS Library 1.1.1
12367  * Copyright(c) 2006-2007, Ext JS, LLC.
12368  *
12369  * Originally Released Under LGPL - original licence link has changed is not relivant.
12370  *
12371  * Fork - LGPL
12372  * <script type="text/javascript">
12373  */
12374
12375 /**
12376  * @class Roo.data.JsonReader
12377  * @extends Roo.data.DataReader
12378  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12379  * based on mappings in a provided Roo.data.Record constructor.
12380  * 
12381  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12382  * in the reply previously. 
12383  * 
12384  * <p>
12385  * Example code:
12386  * <pre><code>
12387 var RecordDef = Roo.data.Record.create([
12388     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12389     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12390 ]);
12391 var myReader = new Roo.data.JsonReader({
12392     totalProperty: "results",    // The property which contains the total dataset size (optional)
12393     root: "rows",                // The property which contains an Array of row objects
12394     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12395 }, RecordDef);
12396 </code></pre>
12397  * <p>
12398  * This would consume a JSON file like this:
12399  * <pre><code>
12400 { 'results': 2, 'rows': [
12401     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12402     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12403 }
12404 </code></pre>
12405  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12406  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12407  * paged from the remote server.
12408  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12409  * @cfg {String} root name of the property which contains the Array of row objects.
12410  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12411  * @cfg {Array} fields Array of field definition objects
12412  * @constructor
12413  * Create a new JsonReader
12414  * @param {Object} meta Metadata configuration options
12415  * @param {Object} recordType Either an Array of field definition objects,
12416  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12417  */
12418 Roo.data.JsonReader = function(meta, recordType){
12419     
12420     meta = meta || {};
12421     // set some defaults:
12422     Roo.applyIf(meta, {
12423         totalProperty: 'total',
12424         successProperty : 'success',
12425         root : 'data',
12426         id : 'id'
12427     });
12428     
12429     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12430 };
12431 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12432     
12433     /**
12434      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12435      * Used by Store query builder to append _requestMeta to params.
12436      * 
12437      */
12438     metaFromRemote : false,
12439     /**
12440      * This method is only used by a DataProxy which has retrieved data from a remote server.
12441      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12442      * @return {Object} data A data block which is used by an Roo.data.Store object as
12443      * a cache of Roo.data.Records.
12444      */
12445     read : function(response){
12446         var json = response.responseText;
12447        
12448         var o = /* eval:var:o */ eval("("+json+")");
12449         if(!o) {
12450             throw {message: "JsonReader.read: Json object not found"};
12451         }
12452         
12453         if(o.metaData){
12454             
12455             delete this.ef;
12456             this.metaFromRemote = true;
12457             this.meta = o.metaData;
12458             this.recordType = Roo.data.Record.create(o.metaData.fields);
12459             this.onMetaChange(this.meta, this.recordType, o);
12460         }
12461         return this.readRecords(o);
12462     },
12463
12464     // private function a store will implement
12465     onMetaChange : function(meta, recordType, o){
12466
12467     },
12468
12469     /**
12470          * @ignore
12471          */
12472     simpleAccess: function(obj, subsc) {
12473         return obj[subsc];
12474     },
12475
12476         /**
12477          * @ignore
12478          */
12479     getJsonAccessor: function(){
12480         var re = /[\[\.]/;
12481         return function(expr) {
12482             try {
12483                 return(re.test(expr))
12484                     ? new Function("obj", "return obj." + expr)
12485                     : function(obj){
12486                         return obj[expr];
12487                     };
12488             } catch(e){}
12489             return Roo.emptyFn;
12490         };
12491     }(),
12492
12493     /**
12494      * Create a data block containing Roo.data.Records from an XML document.
12495      * @param {Object} o An object which contains an Array of row objects in the property specified
12496      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12497      * which contains the total size of the dataset.
12498      * @return {Object} data A data block which is used by an Roo.data.Store object as
12499      * a cache of Roo.data.Records.
12500      */
12501     readRecords : function(o){
12502         /**
12503          * After any data loads, the raw JSON data is available for further custom processing.
12504          * @type Object
12505          */
12506         this.o = o;
12507         var s = this.meta, Record = this.recordType,
12508             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12509
12510 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12511         if (!this.ef) {
12512             if(s.totalProperty) {
12513                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12514                 }
12515                 if(s.successProperty) {
12516                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12517                 }
12518                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12519                 if (s.id) {
12520                         var g = this.getJsonAccessor(s.id);
12521                         this.getId = function(rec) {
12522                                 var r = g(rec);  
12523                                 return (r === undefined || r === "") ? null : r;
12524                         };
12525                 } else {
12526                         this.getId = function(){return null;};
12527                 }
12528             this.ef = [];
12529             for(var jj = 0; jj < fl; jj++){
12530                 f = fi[jj];
12531                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12532                 this.ef[jj] = this.getJsonAccessor(map);
12533             }
12534         }
12535
12536         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12537         if(s.totalProperty){
12538             var vt = parseInt(this.getTotal(o), 10);
12539             if(!isNaN(vt)){
12540                 totalRecords = vt;
12541             }
12542         }
12543         if(s.successProperty){
12544             var vs = this.getSuccess(o);
12545             if(vs === false || vs === 'false'){
12546                 success = false;
12547             }
12548         }
12549         var records = [];
12550         for(var i = 0; i < c; i++){
12551                 var n = root[i];
12552             var values = {};
12553             var id = this.getId(n);
12554             for(var j = 0; j < fl; j++){
12555                 f = fi[j];
12556             var v = this.ef[j](n);
12557             if (!f.convert) {
12558                 Roo.log('missing convert for ' + f.name);
12559                 Roo.log(f);
12560                 continue;
12561             }
12562             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12563             }
12564             var record = new Record(values, id);
12565             record.json = n;
12566             records[i] = record;
12567         }
12568         return {
12569             raw : o,
12570             success : success,
12571             records : records,
12572             totalRecords : totalRecords
12573         };
12574     }
12575 });/*
12576  * Based on:
12577  * Ext JS Library 1.1.1
12578  * Copyright(c) 2006-2007, Ext JS, LLC.
12579  *
12580  * Originally Released Under LGPL - original licence link has changed is not relivant.
12581  *
12582  * Fork - LGPL
12583  * <script type="text/javascript">
12584  */
12585
12586 /**
12587  * @class Roo.data.ArrayReader
12588  * @extends Roo.data.DataReader
12589  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12590  * Each element of that Array represents a row of data fields. The
12591  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12592  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12593  * <p>
12594  * Example code:.
12595  * <pre><code>
12596 var RecordDef = Roo.data.Record.create([
12597     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12598     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12599 ]);
12600 var myReader = new Roo.data.ArrayReader({
12601     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12602 }, RecordDef);
12603 </code></pre>
12604  * <p>
12605  * This would consume an Array like this:
12606  * <pre><code>
12607 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12608   </code></pre>
12609  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12610  * @constructor
12611  * Create a new JsonReader
12612  * @param {Object} meta Metadata configuration options.
12613  * @param {Object} recordType Either an Array of field definition objects
12614  * as specified to {@link Roo.data.Record#create},
12615  * or an {@link Roo.data.Record} object
12616  * created using {@link Roo.data.Record#create}.
12617  */
12618 Roo.data.ArrayReader = function(meta, recordType){
12619     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12620 };
12621
12622 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12623     /**
12624      * Create a data block containing Roo.data.Records from an XML document.
12625      * @param {Object} o An Array of row objects which represents the dataset.
12626      * @return {Object} data A data block which is used by an Roo.data.Store object as
12627      * a cache of Roo.data.Records.
12628      */
12629     readRecords : function(o){
12630         var sid = this.meta ? this.meta.id : null;
12631         var recordType = this.recordType, fields = recordType.prototype.fields;
12632         var records = [];
12633         var root = o;
12634             for(var i = 0; i < root.length; i++){
12635                     var n = root[i];
12636                 var values = {};
12637                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12638                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12639                 var f = fields.items[j];
12640                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12641                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12642                 v = f.convert(v);
12643                 values[f.name] = v;
12644             }
12645                 var record = new recordType(values, id);
12646                 record.json = n;
12647                 records[records.length] = record;
12648             }
12649             return {
12650                 records : records,
12651                 totalRecords : records.length
12652             };
12653     }
12654 });/*
12655  * - LGPL
12656  * * 
12657  */
12658
12659 /**
12660  * @class Roo.bootstrap.ComboBox
12661  * @extends Roo.bootstrap.TriggerField
12662  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12663  * @cfg {Boolean} append (true|false) default false
12664  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12665  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12666  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12667  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12668  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12669  * @cfg {Boolean} animate default true
12670  * @cfg {Boolean} emptyResultText only for touch device
12671  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12672  * @cfg {String} emptyTitle default ''
12673  * @constructor
12674  * Create a new ComboBox.
12675  * @param {Object} config Configuration options
12676  */
12677 Roo.bootstrap.ComboBox = function(config){
12678     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12679     this.addEvents({
12680         /**
12681          * @event expand
12682          * Fires when the dropdown list is expanded
12683         * @param {Roo.bootstrap.ComboBox} combo This combo box
12684         */
12685         'expand' : true,
12686         /**
12687          * @event collapse
12688          * Fires when the dropdown list is collapsed
12689         * @param {Roo.bootstrap.ComboBox} combo This combo box
12690         */
12691         'collapse' : true,
12692         /**
12693          * @event beforeselect
12694          * Fires before a list item is selected. Return false to cancel the selection.
12695         * @param {Roo.bootstrap.ComboBox} combo This combo box
12696         * @param {Roo.data.Record} record The data record returned from the underlying store
12697         * @param {Number} index The index of the selected item in the dropdown list
12698         */
12699         'beforeselect' : true,
12700         /**
12701          * @event select
12702          * Fires when a list item is selected
12703         * @param {Roo.bootstrap.ComboBox} combo This combo box
12704         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12705         * @param {Number} index The index of the selected item in the dropdown list
12706         */
12707         'select' : true,
12708         /**
12709          * @event beforequery
12710          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12711          * The event object passed has these properties:
12712         * @param {Roo.bootstrap.ComboBox} combo This combo box
12713         * @param {String} query The query
12714         * @param {Boolean} forceAll true to force "all" query
12715         * @param {Boolean} cancel true to cancel the query
12716         * @param {Object} e The query event object
12717         */
12718         'beforequery': true,
12719          /**
12720          * @event add
12721          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12722         * @param {Roo.bootstrap.ComboBox} combo This combo box
12723         */
12724         'add' : true,
12725         /**
12726          * @event edit
12727          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12728         * @param {Roo.bootstrap.ComboBox} combo This combo box
12729         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12730         */
12731         'edit' : true,
12732         /**
12733          * @event remove
12734          * Fires when the remove value from the combobox array
12735         * @param {Roo.bootstrap.ComboBox} combo This combo box
12736         */
12737         'remove' : true,
12738         /**
12739          * @event afterremove
12740          * Fires when the remove value from the combobox array
12741         * @param {Roo.bootstrap.ComboBox} combo This combo box
12742         */
12743         'afterremove' : true,
12744         /**
12745          * @event specialfilter
12746          * Fires when specialfilter
12747             * @param {Roo.bootstrap.ComboBox} combo This combo box
12748             */
12749         'specialfilter' : true,
12750         /**
12751          * @event tick
12752          * Fires when tick the element
12753             * @param {Roo.bootstrap.ComboBox} combo This combo box
12754             */
12755         'tick' : true,
12756         /**
12757          * @event touchviewdisplay
12758          * Fires when touch view require special display (default is using displayField)
12759             * @param {Roo.bootstrap.ComboBox} combo This combo box
12760             * @param {Object} cfg set html .
12761             */
12762         'touchviewdisplay' : true
12763         
12764     });
12765     
12766     this.item = [];
12767     this.tickItems = [];
12768     
12769     this.selectedIndex = -1;
12770     if(this.mode == 'local'){
12771         if(config.queryDelay === undefined){
12772             this.queryDelay = 10;
12773         }
12774         if(config.minChars === undefined){
12775             this.minChars = 0;
12776         }
12777     }
12778 };
12779
12780 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12781      
12782     /**
12783      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12784      * rendering into an Roo.Editor, defaults to false)
12785      */
12786     /**
12787      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12788      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12789      */
12790     /**
12791      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12792      */
12793     /**
12794      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12795      * the dropdown list (defaults to undefined, with no header element)
12796      */
12797
12798      /**
12799      * @cfg {String/Roo.Template} tpl The template to use to render the output
12800      */
12801      
12802      /**
12803      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12804      */
12805     listWidth: undefined,
12806     /**
12807      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12808      * mode = 'remote' or 'text' if mode = 'local')
12809      */
12810     displayField: undefined,
12811     
12812     /**
12813      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12814      * mode = 'remote' or 'value' if mode = 'local'). 
12815      * Note: use of a valueField requires the user make a selection
12816      * in order for a value to be mapped.
12817      */
12818     valueField: undefined,
12819     /**
12820      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12821      */
12822     modalTitle : '',
12823     
12824     /**
12825      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12826      * field's data value (defaults to the underlying DOM element's name)
12827      */
12828     hiddenName: undefined,
12829     /**
12830      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12831      */
12832     listClass: '',
12833     /**
12834      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12835      */
12836     selectedClass: 'active',
12837     
12838     /**
12839      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12840      */
12841     shadow:'sides',
12842     /**
12843      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12844      * anchor positions (defaults to 'tl-bl')
12845      */
12846     listAlign: 'tl-bl?',
12847     /**
12848      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12849      */
12850     maxHeight: 300,
12851     /**
12852      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12853      * query specified by the allQuery config option (defaults to 'query')
12854      */
12855     triggerAction: 'query',
12856     /**
12857      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12858      * (defaults to 4, does not apply if editable = false)
12859      */
12860     minChars : 4,
12861     /**
12862      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12863      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12864      */
12865     typeAhead: false,
12866     /**
12867      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12868      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12869      */
12870     queryDelay: 500,
12871     /**
12872      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12873      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12874      */
12875     pageSize: 0,
12876     /**
12877      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12878      * when editable = true (defaults to false)
12879      */
12880     selectOnFocus:false,
12881     /**
12882      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12883      */
12884     queryParam: 'query',
12885     /**
12886      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12887      * when mode = 'remote' (defaults to 'Loading...')
12888      */
12889     loadingText: 'Loading...',
12890     /**
12891      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12892      */
12893     resizable: false,
12894     /**
12895      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12896      */
12897     handleHeight : 8,
12898     /**
12899      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12900      * traditional select (defaults to true)
12901      */
12902     editable: true,
12903     /**
12904      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12905      */
12906     allQuery: '',
12907     /**
12908      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12909      */
12910     mode: 'remote',
12911     /**
12912      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12913      * listWidth has a higher value)
12914      */
12915     minListWidth : 70,
12916     /**
12917      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12918      * allow the user to set arbitrary text into the field (defaults to false)
12919      */
12920     forceSelection:false,
12921     /**
12922      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12923      * if typeAhead = true (defaults to 250)
12924      */
12925     typeAheadDelay : 250,
12926     /**
12927      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12928      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12929      */
12930     valueNotFoundText : undefined,
12931     /**
12932      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12933      */
12934     blockFocus : false,
12935     
12936     /**
12937      * @cfg {Boolean} disableClear Disable showing of clear button.
12938      */
12939     disableClear : false,
12940     /**
12941      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12942      */
12943     alwaysQuery : false,
12944     
12945     /**
12946      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12947      */
12948     multiple : false,
12949     
12950     /**
12951      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12952      */
12953     invalidClass : "has-warning",
12954     
12955     /**
12956      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12957      */
12958     validClass : "has-success",
12959     
12960     /**
12961      * @cfg {Boolean} specialFilter (true|false) special filter default false
12962      */
12963     specialFilter : false,
12964     
12965     /**
12966      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12967      */
12968     mobileTouchView : true,
12969     
12970     /**
12971      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12972      */
12973     useNativeIOS : false,
12974     
12975     ios_options : false,
12976     
12977     //private
12978     addicon : false,
12979     editicon: false,
12980     
12981     page: 0,
12982     hasQuery: false,
12983     append: false,
12984     loadNext: false,
12985     autoFocus : true,
12986     tickable : false,
12987     btnPosition : 'right',
12988     triggerList : true,
12989     showToggleBtn : true,
12990     animate : true,
12991     emptyResultText: 'Empty',
12992     triggerText : 'Select',
12993     emptyTitle : '',
12994     
12995     // element that contains real text value.. (when hidden is used..)
12996     
12997     getAutoCreate : function()
12998     {   
12999         var cfg = false;
13000         //render
13001         /*
13002          * Render classic select for iso
13003          */
13004         
13005         if(Roo.isIOS && this.useNativeIOS){
13006             cfg = this.getAutoCreateNativeIOS();
13007             return cfg;
13008         }
13009         
13010         /*
13011          * Touch Devices
13012          */
13013         
13014         if(Roo.isTouch && this.mobileTouchView){
13015             cfg = this.getAutoCreateTouchView();
13016             return cfg;;
13017         }
13018         
13019         /*
13020          *  Normal ComboBox
13021          */
13022         if(!this.tickable){
13023             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13024             return cfg;
13025         }
13026         
13027         /*
13028          *  ComboBox with tickable selections
13029          */
13030              
13031         var align = this.labelAlign || this.parentLabelAlign();
13032         
13033         cfg = {
13034             cls : 'form-group roo-combobox-tickable' //input-group
13035         };
13036         
13037         var btn_text_select = '';
13038         var btn_text_done = '';
13039         var btn_text_cancel = '';
13040         
13041         if (this.btn_text_show) {
13042             btn_text_select = 'Select';
13043             btn_text_done = 'Done';
13044             btn_text_cancel = 'Cancel'; 
13045         }
13046         
13047         var buttons = {
13048             tag : 'div',
13049             cls : 'tickable-buttons',
13050             cn : [
13051                 {
13052                     tag : 'button',
13053                     type : 'button',
13054                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13055                     //html : this.triggerText
13056                     html: btn_text_select
13057                 },
13058                 {
13059                     tag : 'button',
13060                     type : 'button',
13061                     name : 'ok',
13062                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13063                     //html : 'Done'
13064                     html: btn_text_done
13065                 },
13066                 {
13067                     tag : 'button',
13068                     type : 'button',
13069                     name : 'cancel',
13070                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13071                     //html : 'Cancel'
13072                     html: btn_text_cancel
13073                 }
13074             ]
13075         };
13076         
13077         if(this.editable){
13078             buttons.cn.unshift({
13079                 tag: 'input',
13080                 cls: 'roo-select2-search-field-input'
13081             });
13082         }
13083         
13084         var _this = this;
13085         
13086         Roo.each(buttons.cn, function(c){
13087             if (_this.size) {
13088                 c.cls += ' btn-' + _this.size;
13089             }
13090
13091             if (_this.disabled) {
13092                 c.disabled = true;
13093             }
13094         });
13095         
13096         var box = {
13097             tag: 'div',
13098             cn: [
13099                 {
13100                     tag: 'input',
13101                     type : 'hidden',
13102                     cls: 'form-hidden-field'
13103                 },
13104                 {
13105                     tag: 'ul',
13106                     cls: 'roo-select2-choices',
13107                     cn:[
13108                         {
13109                             tag: 'li',
13110                             cls: 'roo-select2-search-field',
13111                             cn: [
13112                                 buttons
13113                             ]
13114                         }
13115                     ]
13116                 }
13117             ]
13118         };
13119         
13120         var combobox = {
13121             cls: 'roo-select2-container input-group roo-select2-container-multi',
13122             cn: [
13123                 box
13124 //                {
13125 //                    tag: 'ul',
13126 //                    cls: 'typeahead typeahead-long dropdown-menu',
13127 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13128 //                }
13129             ]
13130         };
13131         
13132         if(this.hasFeedback && !this.allowBlank){
13133             
13134             var feedback = {
13135                 tag: 'span',
13136                 cls: 'glyphicon form-control-feedback'
13137             };
13138
13139             combobox.cn.push(feedback);
13140         }
13141         
13142         
13143         if (align ==='left' && this.fieldLabel.length) {
13144             
13145             cfg.cls += ' roo-form-group-label-left';
13146             
13147             cfg.cn = [
13148                 {
13149                     tag : 'i',
13150                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13151                     tooltip : 'This field is required'
13152                 },
13153                 {
13154                     tag: 'label',
13155                     'for' :  id,
13156                     cls : 'control-label',
13157                     html : this.fieldLabel
13158
13159                 },
13160                 {
13161                     cls : "", 
13162                     cn: [
13163                         combobox
13164                     ]
13165                 }
13166
13167             ];
13168             
13169             var labelCfg = cfg.cn[1];
13170             var contentCfg = cfg.cn[2];
13171             
13172
13173             if(this.indicatorpos == 'right'){
13174                 
13175                 cfg.cn = [
13176                     {
13177                         tag: 'label',
13178                         'for' :  id,
13179                         cls : 'control-label',
13180                         cn : [
13181                             {
13182                                 tag : 'span',
13183                                 html : this.fieldLabel
13184                             },
13185                             {
13186                                 tag : 'i',
13187                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13188                                 tooltip : 'This field is required'
13189                             }
13190                         ]
13191                     },
13192                     {
13193                         cls : "",
13194                         cn: [
13195                             combobox
13196                         ]
13197                     }
13198
13199                 ];
13200                 
13201                 
13202                 
13203                 labelCfg = cfg.cn[0];
13204                 contentCfg = cfg.cn[1];
13205             
13206             }
13207             
13208             if(this.labelWidth > 12){
13209                 labelCfg.style = "width: " + this.labelWidth + 'px';
13210             }
13211             
13212             if(this.labelWidth < 13 && this.labelmd == 0){
13213                 this.labelmd = this.labelWidth;
13214             }
13215             
13216             if(this.labellg > 0){
13217                 labelCfg.cls += ' col-lg-' + this.labellg;
13218                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13219             }
13220             
13221             if(this.labelmd > 0){
13222                 labelCfg.cls += ' col-md-' + this.labelmd;
13223                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13224             }
13225             
13226             if(this.labelsm > 0){
13227                 labelCfg.cls += ' col-sm-' + this.labelsm;
13228                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13229             }
13230             
13231             if(this.labelxs > 0){
13232                 labelCfg.cls += ' col-xs-' + this.labelxs;
13233                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13234             }
13235                 
13236                 
13237         } else if ( this.fieldLabel.length) {
13238 //                Roo.log(" label");
13239                  cfg.cn = [
13240                     {
13241                         tag : 'i',
13242                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13243                         tooltip : 'This field is required'
13244                     },
13245                     {
13246                         tag: 'label',
13247                         //cls : 'input-group-addon',
13248                         html : this.fieldLabel
13249                     },
13250                     combobox
13251                 ];
13252                 
13253                 if(this.indicatorpos == 'right'){
13254                     cfg.cn = [
13255                         {
13256                             tag: 'label',
13257                             //cls : 'input-group-addon',
13258                             html : this.fieldLabel
13259                         },
13260                         {
13261                             tag : 'i',
13262                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13263                             tooltip : 'This field is required'
13264                         },
13265                         combobox
13266                     ];
13267                     
13268                 }
13269
13270         } else {
13271             
13272 //                Roo.log(" no label && no align");
13273                 cfg = combobox
13274                      
13275                 
13276         }
13277          
13278         var settings=this;
13279         ['xs','sm','md','lg'].map(function(size){
13280             if (settings[size]) {
13281                 cfg.cls += ' col-' + size + '-' + settings[size];
13282             }
13283         });
13284         
13285         return cfg;
13286         
13287     },
13288     
13289     _initEventsCalled : false,
13290     
13291     // private
13292     initEvents: function()
13293     {   
13294         if (this._initEventsCalled) { // as we call render... prevent looping...
13295             return;
13296         }
13297         this._initEventsCalled = true;
13298         
13299         if (!this.store) {
13300             throw "can not find store for combo";
13301         }
13302         
13303         this.indicator = this.indicatorEl();
13304         
13305         this.store = Roo.factory(this.store, Roo.data);
13306         this.store.parent = this;
13307         
13308         // if we are building from html. then this element is so complex, that we can not really
13309         // use the rendered HTML.
13310         // so we have to trash and replace the previous code.
13311         if (Roo.XComponent.build_from_html) {
13312             // remove this element....
13313             var e = this.el.dom, k=0;
13314             while (e ) { e = e.previousSibling;  ++k;}
13315
13316             this.el.remove();
13317             
13318             this.el=false;
13319             this.rendered = false;
13320             
13321             this.render(this.parent().getChildContainer(true), k);
13322         }
13323         
13324         if(Roo.isIOS && this.useNativeIOS){
13325             this.initIOSView();
13326             return;
13327         }
13328         
13329         /*
13330          * Touch Devices
13331          */
13332         
13333         if(Roo.isTouch && this.mobileTouchView){
13334             this.initTouchView();
13335             return;
13336         }
13337         
13338         if(this.tickable){
13339             this.initTickableEvents();
13340             return;
13341         }
13342         
13343         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13344         
13345         if(this.hiddenName){
13346             
13347             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13348             
13349             this.hiddenField.dom.value =
13350                 this.hiddenValue !== undefined ? this.hiddenValue :
13351                 this.value !== undefined ? this.value : '';
13352
13353             // prevent input submission
13354             this.el.dom.removeAttribute('name');
13355             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13356              
13357              
13358         }
13359         //if(Roo.isGecko){
13360         //    this.el.dom.setAttribute('autocomplete', 'off');
13361         //}
13362         
13363         var cls = 'x-combo-list';
13364         
13365         //this.list = new Roo.Layer({
13366         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13367         //});
13368         
13369         var _this = this;
13370         
13371         (function(){
13372             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13373             _this.list.setWidth(lw);
13374         }).defer(100);
13375         
13376         this.list.on('mouseover', this.onViewOver, this);
13377         this.list.on('mousemove', this.onViewMove, this);
13378         this.list.on('scroll', this.onViewScroll, this);
13379         
13380         /*
13381         this.list.swallowEvent('mousewheel');
13382         this.assetHeight = 0;
13383
13384         if(this.title){
13385             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13386             this.assetHeight += this.header.getHeight();
13387         }
13388
13389         this.innerList = this.list.createChild({cls:cls+'-inner'});
13390         this.innerList.on('mouseover', this.onViewOver, this);
13391         this.innerList.on('mousemove', this.onViewMove, this);
13392         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13393         
13394         if(this.allowBlank && !this.pageSize && !this.disableClear){
13395             this.footer = this.list.createChild({cls:cls+'-ft'});
13396             this.pageTb = new Roo.Toolbar(this.footer);
13397            
13398         }
13399         if(this.pageSize){
13400             this.footer = this.list.createChild({cls:cls+'-ft'});
13401             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13402                     {pageSize: this.pageSize});
13403             
13404         }
13405         
13406         if (this.pageTb && this.allowBlank && !this.disableClear) {
13407             var _this = this;
13408             this.pageTb.add(new Roo.Toolbar.Fill(), {
13409                 cls: 'x-btn-icon x-btn-clear',
13410                 text: '&#160;',
13411                 handler: function()
13412                 {
13413                     _this.collapse();
13414                     _this.clearValue();
13415                     _this.onSelect(false, -1);
13416                 }
13417             });
13418         }
13419         if (this.footer) {
13420             this.assetHeight += this.footer.getHeight();
13421         }
13422         */
13423             
13424         if(!this.tpl){
13425             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13426         }
13427
13428         this.view = new Roo.View(this.list, this.tpl, {
13429             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13430         });
13431         //this.view.wrapEl.setDisplayed(false);
13432         this.view.on('click', this.onViewClick, this);
13433         
13434         
13435         this.store.on('beforeload', this.onBeforeLoad, this);
13436         this.store.on('load', this.onLoad, this);
13437         this.store.on('loadexception', this.onLoadException, this);
13438         /*
13439         if(this.resizable){
13440             this.resizer = new Roo.Resizable(this.list,  {
13441                pinned:true, handles:'se'
13442             });
13443             this.resizer.on('resize', function(r, w, h){
13444                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13445                 this.listWidth = w;
13446                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13447                 this.restrictHeight();
13448             }, this);
13449             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13450         }
13451         */
13452         if(!this.editable){
13453             this.editable = true;
13454             this.setEditable(false);
13455         }
13456         
13457         /*
13458         
13459         if (typeof(this.events.add.listeners) != 'undefined') {
13460             
13461             this.addicon = this.wrap.createChild(
13462                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13463        
13464             this.addicon.on('click', function(e) {
13465                 this.fireEvent('add', this);
13466             }, this);
13467         }
13468         if (typeof(this.events.edit.listeners) != 'undefined') {
13469             
13470             this.editicon = this.wrap.createChild(
13471                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13472             if (this.addicon) {
13473                 this.editicon.setStyle('margin-left', '40px');
13474             }
13475             this.editicon.on('click', function(e) {
13476                 
13477                 // we fire even  if inothing is selected..
13478                 this.fireEvent('edit', this, this.lastData );
13479                 
13480             }, this);
13481         }
13482         */
13483         
13484         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13485             "up" : function(e){
13486                 this.inKeyMode = true;
13487                 this.selectPrev();
13488             },
13489
13490             "down" : function(e){
13491                 if(!this.isExpanded()){
13492                     this.onTriggerClick();
13493                 }else{
13494                     this.inKeyMode = true;
13495                     this.selectNext();
13496                 }
13497             },
13498
13499             "enter" : function(e){
13500 //                this.onViewClick();
13501                 //return true;
13502                 this.collapse();
13503                 
13504                 if(this.fireEvent("specialkey", this, e)){
13505                     this.onViewClick(false);
13506                 }
13507                 
13508                 return true;
13509             },
13510
13511             "esc" : function(e){
13512                 this.collapse();
13513             },
13514
13515             "tab" : function(e){
13516                 this.collapse();
13517                 
13518                 if(this.fireEvent("specialkey", this, e)){
13519                     this.onViewClick(false);
13520                 }
13521                 
13522                 return true;
13523             },
13524
13525             scope : this,
13526
13527             doRelay : function(foo, bar, hname){
13528                 if(hname == 'down' || this.scope.isExpanded()){
13529                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13530                 }
13531                 return true;
13532             },
13533
13534             forceKeyDown: true
13535         });
13536         
13537         
13538         this.queryDelay = Math.max(this.queryDelay || 10,
13539                 this.mode == 'local' ? 10 : 250);
13540         
13541         
13542         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13543         
13544         if(this.typeAhead){
13545             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13546         }
13547         if(this.editable !== false){
13548             this.inputEl().on("keyup", this.onKeyUp, this);
13549         }
13550         if(this.forceSelection){
13551             this.inputEl().on('blur', this.doForce, this);
13552         }
13553         
13554         if(this.multiple){
13555             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13556             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13557         }
13558     },
13559     
13560     initTickableEvents: function()
13561     {   
13562         this.createList();
13563         
13564         if(this.hiddenName){
13565             
13566             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13567             
13568             this.hiddenField.dom.value =
13569                 this.hiddenValue !== undefined ? this.hiddenValue :
13570                 this.value !== undefined ? this.value : '';
13571
13572             // prevent input submission
13573             this.el.dom.removeAttribute('name');
13574             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13575              
13576              
13577         }
13578         
13579 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13580         
13581         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13582         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13583         if(this.triggerList){
13584             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13585         }
13586          
13587         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13588         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13589         
13590         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13591         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13592         
13593         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13594         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13595         
13596         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13597         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13598         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13599         
13600         this.okBtn.hide();
13601         this.cancelBtn.hide();
13602         
13603         var _this = this;
13604         
13605         (function(){
13606             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13607             _this.list.setWidth(lw);
13608         }).defer(100);
13609         
13610         this.list.on('mouseover', this.onViewOver, this);
13611         this.list.on('mousemove', this.onViewMove, this);
13612         
13613         this.list.on('scroll', this.onViewScroll, this);
13614         
13615         if(!this.tpl){
13616             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13617                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13618         }
13619
13620         this.view = new Roo.View(this.list, this.tpl, {
13621             singleSelect:true,
13622             tickable:true,
13623             parent:this,
13624             store: this.store,
13625             selectedClass: this.selectedClass
13626         });
13627         
13628         //this.view.wrapEl.setDisplayed(false);
13629         this.view.on('click', this.onViewClick, this);
13630         
13631         
13632         
13633         this.store.on('beforeload', this.onBeforeLoad, this);
13634         this.store.on('load', this.onLoad, this);
13635         this.store.on('loadexception', this.onLoadException, this);
13636         
13637         if(this.editable){
13638             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13639                 "up" : function(e){
13640                     this.inKeyMode = true;
13641                     this.selectPrev();
13642                 },
13643
13644                 "down" : function(e){
13645                     this.inKeyMode = true;
13646                     this.selectNext();
13647                 },
13648
13649                 "enter" : function(e){
13650                     if(this.fireEvent("specialkey", this, e)){
13651                         this.onViewClick(false);
13652                     }
13653                     
13654                     return true;
13655                 },
13656
13657                 "esc" : function(e){
13658                     this.onTickableFooterButtonClick(e, false, false);
13659                 },
13660
13661                 "tab" : function(e){
13662                     this.fireEvent("specialkey", this, e);
13663                     
13664                     this.onTickableFooterButtonClick(e, false, false);
13665                     
13666                     return true;
13667                 },
13668
13669                 scope : this,
13670
13671                 doRelay : function(e, fn, key){
13672                     if(this.scope.isExpanded()){
13673                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13674                     }
13675                     return true;
13676                 },
13677
13678                 forceKeyDown: true
13679             });
13680         }
13681         
13682         this.queryDelay = Math.max(this.queryDelay || 10,
13683                 this.mode == 'local' ? 10 : 250);
13684         
13685         
13686         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13687         
13688         if(this.typeAhead){
13689             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13690         }
13691         
13692         if(this.editable !== false){
13693             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13694         }
13695         
13696         this.indicator = this.indicatorEl();
13697         
13698         if(this.indicator){
13699             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13700             this.indicator.hide();
13701         }
13702         
13703     },
13704
13705     onDestroy : function(){
13706         if(this.view){
13707             this.view.setStore(null);
13708             this.view.el.removeAllListeners();
13709             this.view.el.remove();
13710             this.view.purgeListeners();
13711         }
13712         if(this.list){
13713             this.list.dom.innerHTML  = '';
13714         }
13715         
13716         if(this.store){
13717             this.store.un('beforeload', this.onBeforeLoad, this);
13718             this.store.un('load', this.onLoad, this);
13719             this.store.un('loadexception', this.onLoadException, this);
13720         }
13721         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13722     },
13723
13724     // private
13725     fireKey : function(e){
13726         if(e.isNavKeyPress() && !this.list.isVisible()){
13727             this.fireEvent("specialkey", this, e);
13728         }
13729     },
13730
13731     // private
13732     onResize: function(w, h){
13733 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13734 //        
13735 //        if(typeof w != 'number'){
13736 //            // we do not handle it!?!?
13737 //            return;
13738 //        }
13739 //        var tw = this.trigger.getWidth();
13740 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13741 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13742 //        var x = w - tw;
13743 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13744 //            
13745 //        //this.trigger.setStyle('left', x+'px');
13746 //        
13747 //        if(this.list && this.listWidth === undefined){
13748 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13749 //            this.list.setWidth(lw);
13750 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13751 //        }
13752         
13753     
13754         
13755     },
13756
13757     /**
13758      * Allow or prevent the user from directly editing the field text.  If false is passed,
13759      * the user will only be able to select from the items defined in the dropdown list.  This method
13760      * is the runtime equivalent of setting the 'editable' config option at config time.
13761      * @param {Boolean} value True to allow the user to directly edit the field text
13762      */
13763     setEditable : function(value){
13764         if(value == this.editable){
13765             return;
13766         }
13767         this.editable = value;
13768         if(!value){
13769             this.inputEl().dom.setAttribute('readOnly', true);
13770             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13771             this.inputEl().addClass('x-combo-noedit');
13772         }else{
13773             this.inputEl().dom.setAttribute('readOnly', false);
13774             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13775             this.inputEl().removeClass('x-combo-noedit');
13776         }
13777     },
13778
13779     // private
13780     
13781     onBeforeLoad : function(combo,opts){
13782         if(!this.hasFocus){
13783             return;
13784         }
13785          if (!opts.add) {
13786             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13787          }
13788         this.restrictHeight();
13789         this.selectedIndex = -1;
13790     },
13791
13792     // private
13793     onLoad : function(){
13794         
13795         this.hasQuery = false;
13796         
13797         if(!this.hasFocus){
13798             return;
13799         }
13800         
13801         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13802             this.loading.hide();
13803         }
13804         
13805         if(this.store.getCount() > 0){
13806             
13807             this.expand();
13808             this.restrictHeight();
13809             if(this.lastQuery == this.allQuery){
13810                 if(this.editable && !this.tickable){
13811                     this.inputEl().dom.select();
13812                 }
13813                 
13814                 if(
13815                     !this.selectByValue(this.value, true) &&
13816                     this.autoFocus && 
13817                     (
13818                         !this.store.lastOptions ||
13819                         typeof(this.store.lastOptions.add) == 'undefined' || 
13820                         this.store.lastOptions.add != true
13821                     )
13822                 ){
13823                     this.select(0, true);
13824                 }
13825             }else{
13826                 if(this.autoFocus){
13827                     this.selectNext();
13828                 }
13829                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13830                     this.taTask.delay(this.typeAheadDelay);
13831                 }
13832             }
13833         }else{
13834             this.onEmptyResults();
13835         }
13836         
13837         //this.el.focus();
13838     },
13839     // private
13840     onLoadException : function()
13841     {
13842         this.hasQuery = false;
13843         
13844         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13845             this.loading.hide();
13846         }
13847         
13848         if(this.tickable && this.editable){
13849             return;
13850         }
13851         
13852         this.collapse();
13853         // only causes errors at present
13854         //Roo.log(this.store.reader.jsonData);
13855         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13856             // fixme
13857             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13858         //}
13859         
13860         
13861     },
13862     // private
13863     onTypeAhead : function(){
13864         if(this.store.getCount() > 0){
13865             var r = this.store.getAt(0);
13866             var newValue = r.data[this.displayField];
13867             var len = newValue.length;
13868             var selStart = this.getRawValue().length;
13869             
13870             if(selStart != len){
13871                 this.setRawValue(newValue);
13872                 this.selectText(selStart, newValue.length);
13873             }
13874         }
13875     },
13876
13877     // private
13878     onSelect : function(record, index){
13879         
13880         if(this.fireEvent('beforeselect', this, record, index) !== false){
13881         
13882             this.setFromData(index > -1 ? record.data : false);
13883             
13884             this.collapse();
13885             this.fireEvent('select', this, record, index);
13886         }
13887     },
13888
13889     /**
13890      * Returns the currently selected field value or empty string if no value is set.
13891      * @return {String} value The selected value
13892      */
13893     getValue : function()
13894     {
13895         if(Roo.isIOS && this.useNativeIOS){
13896             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13897         }
13898         
13899         if(this.multiple){
13900             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13901         }
13902         
13903         if(this.valueField){
13904             return typeof this.value != 'undefined' ? this.value : '';
13905         }else{
13906             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13907         }
13908     },
13909     
13910     getRawValue : function()
13911     {
13912         if(Roo.isIOS && this.useNativeIOS){
13913             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13914         }
13915         
13916         var v = this.inputEl().getValue();
13917         
13918         return v;
13919     },
13920
13921     /**
13922      * Clears any text/value currently set in the field
13923      */
13924     clearValue : function(){
13925         
13926         if(this.hiddenField){
13927             this.hiddenField.dom.value = '';
13928         }
13929         this.value = '';
13930         this.setRawValue('');
13931         this.lastSelectionText = '';
13932         this.lastData = false;
13933         
13934         var close = this.closeTriggerEl();
13935         
13936         if(close){
13937             close.hide();
13938         }
13939         
13940         this.validate();
13941         
13942     },
13943
13944     /**
13945      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13946      * will be displayed in the field.  If the value does not match the data value of an existing item,
13947      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13948      * Otherwise the field will be blank (although the value will still be set).
13949      * @param {String} value The value to match
13950      */
13951     setValue : function(v)
13952     {
13953         if(Roo.isIOS && this.useNativeIOS){
13954             this.setIOSValue(v);
13955             return;
13956         }
13957         
13958         if(this.multiple){
13959             this.syncValue();
13960             return;
13961         }
13962         
13963         var text = v;
13964         if(this.valueField){
13965             var r = this.findRecord(this.valueField, v);
13966             if(r){
13967                 text = r.data[this.displayField];
13968             }else if(this.valueNotFoundText !== undefined){
13969                 text = this.valueNotFoundText;
13970             }
13971         }
13972         this.lastSelectionText = text;
13973         if(this.hiddenField){
13974             this.hiddenField.dom.value = v;
13975         }
13976         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13977         this.value = v;
13978         
13979         var close = this.closeTriggerEl();
13980         
13981         if(close){
13982             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13983         }
13984         
13985         this.validate();
13986     },
13987     /**
13988      * @property {Object} the last set data for the element
13989      */
13990     
13991     lastData : false,
13992     /**
13993      * Sets the value of the field based on a object which is related to the record format for the store.
13994      * @param {Object} value the value to set as. or false on reset?
13995      */
13996     setFromData : function(o){
13997         
13998         if(this.multiple){
13999             this.addItem(o);
14000             return;
14001         }
14002             
14003         var dv = ''; // display value
14004         var vv = ''; // value value..
14005         this.lastData = o;
14006         if (this.displayField) {
14007             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14008         } else {
14009             // this is an error condition!!!
14010             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14011         }
14012         
14013         if(this.valueField){
14014             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14015         }
14016         
14017         var close = this.closeTriggerEl();
14018         
14019         if(close){
14020             if(dv.length || vv * 1 > 0){
14021                 close.show() ;
14022                 this.blockFocus=true;
14023             } else {
14024                 close.hide();
14025             }             
14026         }
14027         
14028         if(this.hiddenField){
14029             this.hiddenField.dom.value = vv;
14030             
14031             this.lastSelectionText = dv;
14032             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14033             this.value = vv;
14034             return;
14035         }
14036         // no hidden field.. - we store the value in 'value', but still display
14037         // display field!!!!
14038         this.lastSelectionText = dv;
14039         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14040         this.value = vv;
14041         
14042         
14043         
14044     },
14045     // private
14046     reset : function(){
14047         // overridden so that last data is reset..
14048         
14049         if(this.multiple){
14050             this.clearItem();
14051             return;
14052         }
14053         
14054         this.setValue(this.originalValue);
14055         //this.clearInvalid();
14056         this.lastData = false;
14057         if (this.view) {
14058             this.view.clearSelections();
14059         }
14060         
14061         this.validate();
14062     },
14063     // private
14064     findRecord : function(prop, value){
14065         var record;
14066         if(this.store.getCount() > 0){
14067             this.store.each(function(r){
14068                 if(r.data[prop] == value){
14069                     record = r;
14070                     return false;
14071                 }
14072                 return true;
14073             });
14074         }
14075         return record;
14076     },
14077     
14078     getName: function()
14079     {
14080         // returns hidden if it's set..
14081         if (!this.rendered) {return ''};
14082         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14083         
14084     },
14085     // private
14086     onViewMove : function(e, t){
14087         this.inKeyMode = false;
14088     },
14089
14090     // private
14091     onViewOver : function(e, t){
14092         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14093             return;
14094         }
14095         var item = this.view.findItemFromChild(t);
14096         
14097         if(item){
14098             var index = this.view.indexOf(item);
14099             this.select(index, false);
14100         }
14101     },
14102
14103     // private
14104     onViewClick : function(view, doFocus, el, e)
14105     {
14106         var index = this.view.getSelectedIndexes()[0];
14107         
14108         var r = this.store.getAt(index);
14109         
14110         if(this.tickable){
14111             
14112             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14113                 return;
14114             }
14115             
14116             var rm = false;
14117             var _this = this;
14118             
14119             Roo.each(this.tickItems, function(v,k){
14120                 
14121                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14122                     Roo.log(v);
14123                     _this.tickItems.splice(k, 1);
14124                     
14125                     if(typeof(e) == 'undefined' && view == false){
14126                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14127                     }
14128                     
14129                     rm = true;
14130                     return;
14131                 }
14132             });
14133             
14134             if(rm){
14135                 return;
14136             }
14137             
14138             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14139                 this.tickItems.push(r.data);
14140             }
14141             
14142             if(typeof(e) == 'undefined' && view == false){
14143                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14144             }
14145                     
14146             return;
14147         }
14148         
14149         if(r){
14150             this.onSelect(r, index);
14151         }
14152         if(doFocus !== false && !this.blockFocus){
14153             this.inputEl().focus();
14154         }
14155     },
14156
14157     // private
14158     restrictHeight : function(){
14159         //this.innerList.dom.style.height = '';
14160         //var inner = this.innerList.dom;
14161         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14162         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14163         //this.list.beginUpdate();
14164         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14165         this.list.alignTo(this.inputEl(), this.listAlign);
14166         this.list.alignTo(this.inputEl(), this.listAlign);
14167         //this.list.endUpdate();
14168     },
14169
14170     // private
14171     onEmptyResults : function(){
14172         
14173         if(this.tickable && this.editable){
14174             this.hasFocus = false;
14175             this.restrictHeight();
14176             return;
14177         }
14178         
14179         this.collapse();
14180     },
14181
14182     /**
14183      * Returns true if the dropdown list is expanded, else false.
14184      */
14185     isExpanded : function(){
14186         return this.list.isVisible();
14187     },
14188
14189     /**
14190      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14191      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14192      * @param {String} value The data value of the item to select
14193      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14194      * selected item if it is not currently in view (defaults to true)
14195      * @return {Boolean} True if the value matched an item in the list, else false
14196      */
14197     selectByValue : function(v, scrollIntoView){
14198         if(v !== undefined && v !== null){
14199             var r = this.findRecord(this.valueField || this.displayField, v);
14200             if(r){
14201                 this.select(this.store.indexOf(r), scrollIntoView);
14202                 return true;
14203             }
14204         }
14205         return false;
14206     },
14207
14208     /**
14209      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14210      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14211      * @param {Number} index The zero-based index of the list item to select
14212      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14213      * selected item if it is not currently in view (defaults to true)
14214      */
14215     select : function(index, scrollIntoView){
14216         this.selectedIndex = index;
14217         this.view.select(index);
14218         if(scrollIntoView !== false){
14219             var el = this.view.getNode(index);
14220             /*
14221              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14222              */
14223             if(el){
14224                 this.list.scrollChildIntoView(el, false);
14225             }
14226         }
14227     },
14228
14229     // private
14230     selectNext : function(){
14231         var ct = this.store.getCount();
14232         if(ct > 0){
14233             if(this.selectedIndex == -1){
14234                 this.select(0);
14235             }else if(this.selectedIndex < ct-1){
14236                 this.select(this.selectedIndex+1);
14237             }
14238         }
14239     },
14240
14241     // private
14242     selectPrev : function(){
14243         var ct = this.store.getCount();
14244         if(ct > 0){
14245             if(this.selectedIndex == -1){
14246                 this.select(0);
14247             }else if(this.selectedIndex != 0){
14248                 this.select(this.selectedIndex-1);
14249             }
14250         }
14251     },
14252
14253     // private
14254     onKeyUp : function(e){
14255         if(this.editable !== false && !e.isSpecialKey()){
14256             this.lastKey = e.getKey();
14257             this.dqTask.delay(this.queryDelay);
14258         }
14259     },
14260
14261     // private
14262     validateBlur : function(){
14263         return !this.list || !this.list.isVisible();   
14264     },
14265
14266     // private
14267     initQuery : function(){
14268         
14269         var v = this.getRawValue();
14270         
14271         if(this.tickable && this.editable){
14272             v = this.tickableInputEl().getValue();
14273         }
14274         
14275         this.doQuery(v);
14276     },
14277
14278     // private
14279     doForce : function(){
14280         if(this.inputEl().dom.value.length > 0){
14281             this.inputEl().dom.value =
14282                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14283              
14284         }
14285     },
14286
14287     /**
14288      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14289      * query allowing the query action to be canceled if needed.
14290      * @param {String} query The SQL query to execute
14291      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14292      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14293      * saved in the current store (defaults to false)
14294      */
14295     doQuery : function(q, forceAll){
14296         
14297         if(q === undefined || q === null){
14298             q = '';
14299         }
14300         var qe = {
14301             query: q,
14302             forceAll: forceAll,
14303             combo: this,
14304             cancel:false
14305         };
14306         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14307             return false;
14308         }
14309         q = qe.query;
14310         
14311         forceAll = qe.forceAll;
14312         if(forceAll === true || (q.length >= this.minChars)){
14313             
14314             this.hasQuery = true;
14315             
14316             if(this.lastQuery != q || this.alwaysQuery){
14317                 this.lastQuery = q;
14318                 if(this.mode == 'local'){
14319                     this.selectedIndex = -1;
14320                     if(forceAll){
14321                         this.store.clearFilter();
14322                     }else{
14323                         
14324                         if(this.specialFilter){
14325                             this.fireEvent('specialfilter', this);
14326                             this.onLoad();
14327                             return;
14328                         }
14329                         
14330                         this.store.filter(this.displayField, q);
14331                     }
14332                     
14333                     this.store.fireEvent("datachanged", this.store);
14334                     
14335                     this.onLoad();
14336                     
14337                     
14338                 }else{
14339                     
14340                     this.store.baseParams[this.queryParam] = q;
14341                     
14342                     var options = {params : this.getParams(q)};
14343                     
14344                     if(this.loadNext){
14345                         options.add = true;
14346                         options.params.start = this.page * this.pageSize;
14347                     }
14348                     
14349                     this.store.load(options);
14350                     
14351                     /*
14352                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14353                      *  we should expand the list on onLoad
14354                      *  so command out it
14355                      */
14356 //                    this.expand();
14357                 }
14358             }else{
14359                 this.selectedIndex = -1;
14360                 this.onLoad();   
14361             }
14362         }
14363         
14364         this.loadNext = false;
14365     },
14366     
14367     // private
14368     getParams : function(q){
14369         var p = {};
14370         //p[this.queryParam] = q;
14371         
14372         if(this.pageSize){
14373             p.start = 0;
14374             p.limit = this.pageSize;
14375         }
14376         return p;
14377     },
14378
14379     /**
14380      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14381      */
14382     collapse : function(){
14383         if(!this.isExpanded()){
14384             return;
14385         }
14386         
14387         this.list.hide();
14388         
14389         this.hasFocus = false;
14390         
14391         if(this.tickable){
14392             this.okBtn.hide();
14393             this.cancelBtn.hide();
14394             this.trigger.show();
14395             
14396             if(this.editable){
14397                 this.tickableInputEl().dom.value = '';
14398                 this.tickableInputEl().blur();
14399             }
14400             
14401         }
14402         
14403         Roo.get(document).un('mousedown', this.collapseIf, this);
14404         Roo.get(document).un('mousewheel', this.collapseIf, this);
14405         if (!this.editable) {
14406             Roo.get(document).un('keydown', this.listKeyPress, this);
14407         }
14408         this.fireEvent('collapse', this);
14409         
14410         this.validate();
14411     },
14412
14413     // private
14414     collapseIf : function(e){
14415         var in_combo  = e.within(this.el);
14416         var in_list =  e.within(this.list);
14417         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14418         
14419         if (in_combo || in_list || is_list) {
14420             //e.stopPropagation();
14421             return;
14422         }
14423         
14424         if(this.tickable){
14425             this.onTickableFooterButtonClick(e, false, false);
14426         }
14427
14428         this.collapse();
14429         
14430     },
14431
14432     /**
14433      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14434      */
14435     expand : function(){
14436        
14437         if(this.isExpanded() || !this.hasFocus){
14438             return;
14439         }
14440         
14441         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14442         this.list.setWidth(lw);
14443         
14444         Roo.log('expand');
14445         
14446         this.list.show();
14447         
14448         this.restrictHeight();
14449         
14450         if(this.tickable){
14451             
14452             this.tickItems = Roo.apply([], this.item);
14453             
14454             this.okBtn.show();
14455             this.cancelBtn.show();
14456             this.trigger.hide();
14457             
14458             if(this.editable){
14459                 this.tickableInputEl().focus();
14460             }
14461             
14462         }
14463         
14464         Roo.get(document).on('mousedown', this.collapseIf, this);
14465         Roo.get(document).on('mousewheel', this.collapseIf, this);
14466         if (!this.editable) {
14467             Roo.get(document).on('keydown', this.listKeyPress, this);
14468         }
14469         
14470         this.fireEvent('expand', this);
14471     },
14472
14473     // private
14474     // Implements the default empty TriggerField.onTriggerClick function
14475     onTriggerClick : function(e)
14476     {
14477         Roo.log('trigger click');
14478         
14479         if(this.disabled || !this.triggerList){
14480             return;
14481         }
14482         
14483         this.page = 0;
14484         this.loadNext = false;
14485         
14486         if(this.isExpanded()){
14487             this.collapse();
14488             if (!this.blockFocus) {
14489                 this.inputEl().focus();
14490             }
14491             
14492         }else {
14493             this.hasFocus = true;
14494             if(this.triggerAction == 'all') {
14495                 this.doQuery(this.allQuery, true);
14496             } else {
14497                 this.doQuery(this.getRawValue());
14498             }
14499             if (!this.blockFocus) {
14500                 this.inputEl().focus();
14501             }
14502         }
14503     },
14504     
14505     onTickableTriggerClick : function(e)
14506     {
14507         if(this.disabled){
14508             return;
14509         }
14510         
14511         this.page = 0;
14512         this.loadNext = false;
14513         this.hasFocus = true;
14514         
14515         if(this.triggerAction == 'all') {
14516             this.doQuery(this.allQuery, true);
14517         } else {
14518             this.doQuery(this.getRawValue());
14519         }
14520     },
14521     
14522     onSearchFieldClick : function(e)
14523     {
14524         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14525             this.onTickableFooterButtonClick(e, false, false);
14526             return;
14527         }
14528         
14529         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14530             return;
14531         }
14532         
14533         this.page = 0;
14534         this.loadNext = false;
14535         this.hasFocus = true;
14536         
14537         if(this.triggerAction == 'all') {
14538             this.doQuery(this.allQuery, true);
14539         } else {
14540             this.doQuery(this.getRawValue());
14541         }
14542     },
14543     
14544     listKeyPress : function(e)
14545     {
14546         //Roo.log('listkeypress');
14547         // scroll to first matching element based on key pres..
14548         if (e.isSpecialKey()) {
14549             return false;
14550         }
14551         var k = String.fromCharCode(e.getKey()).toUpperCase();
14552         //Roo.log(k);
14553         var match  = false;
14554         var csel = this.view.getSelectedNodes();
14555         var cselitem = false;
14556         if (csel.length) {
14557             var ix = this.view.indexOf(csel[0]);
14558             cselitem  = this.store.getAt(ix);
14559             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14560                 cselitem = false;
14561             }
14562             
14563         }
14564         
14565         this.store.each(function(v) { 
14566             if (cselitem) {
14567                 // start at existing selection.
14568                 if (cselitem.id == v.id) {
14569                     cselitem = false;
14570                 }
14571                 return true;
14572             }
14573                 
14574             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14575                 match = this.store.indexOf(v);
14576                 return false;
14577             }
14578             return true;
14579         }, this);
14580         
14581         if (match === false) {
14582             return true; // no more action?
14583         }
14584         // scroll to?
14585         this.view.select(match);
14586         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14587         sn.scrollIntoView(sn.dom.parentNode, false);
14588     },
14589     
14590     onViewScroll : function(e, t){
14591         
14592         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){
14593             return;
14594         }
14595         
14596         this.hasQuery = true;
14597         
14598         this.loading = this.list.select('.loading', true).first();
14599         
14600         if(this.loading === null){
14601             this.list.createChild({
14602                 tag: 'div',
14603                 cls: 'loading roo-select2-more-results roo-select2-active',
14604                 html: 'Loading more results...'
14605             });
14606             
14607             this.loading = this.list.select('.loading', true).first();
14608             
14609             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14610             
14611             this.loading.hide();
14612         }
14613         
14614         this.loading.show();
14615         
14616         var _combo = this;
14617         
14618         this.page++;
14619         this.loadNext = true;
14620         
14621         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14622         
14623         return;
14624     },
14625     
14626     addItem : function(o)
14627     {   
14628         var dv = ''; // display value
14629         
14630         if (this.displayField) {
14631             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14632         } else {
14633             // this is an error condition!!!
14634             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14635         }
14636         
14637         if(!dv.length){
14638             return;
14639         }
14640         
14641         var choice = this.choices.createChild({
14642             tag: 'li',
14643             cls: 'roo-select2-search-choice',
14644             cn: [
14645                 {
14646                     tag: 'div',
14647                     html: dv
14648                 },
14649                 {
14650                     tag: 'a',
14651                     href: '#',
14652                     cls: 'roo-select2-search-choice-close fa fa-times',
14653                     tabindex: '-1'
14654                 }
14655             ]
14656             
14657         }, this.searchField);
14658         
14659         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14660         
14661         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14662         
14663         this.item.push(o);
14664         
14665         this.lastData = o;
14666         
14667         this.syncValue();
14668         
14669         this.inputEl().dom.value = '';
14670         
14671         this.validate();
14672     },
14673     
14674     onRemoveItem : function(e, _self, o)
14675     {
14676         e.preventDefault();
14677         
14678         this.lastItem = Roo.apply([], this.item);
14679         
14680         var index = this.item.indexOf(o.data) * 1;
14681         
14682         if( index < 0){
14683             Roo.log('not this item?!');
14684             return;
14685         }
14686         
14687         this.item.splice(index, 1);
14688         o.item.remove();
14689         
14690         this.syncValue();
14691         
14692         this.fireEvent('remove', this, e);
14693         
14694         this.validate();
14695         
14696     },
14697     
14698     syncValue : function()
14699     {
14700         if(!this.item.length){
14701             this.clearValue();
14702             return;
14703         }
14704             
14705         var value = [];
14706         var _this = this;
14707         Roo.each(this.item, function(i){
14708             if(_this.valueField){
14709                 value.push(i[_this.valueField]);
14710                 return;
14711             }
14712
14713             value.push(i);
14714         });
14715
14716         this.value = value.join(',');
14717
14718         if(this.hiddenField){
14719             this.hiddenField.dom.value = this.value;
14720         }
14721         
14722         this.store.fireEvent("datachanged", this.store);
14723         
14724         this.validate();
14725     },
14726     
14727     clearItem : function()
14728     {
14729         if(!this.multiple){
14730             return;
14731         }
14732         
14733         this.item = [];
14734         
14735         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14736            c.remove();
14737         });
14738         
14739         this.syncValue();
14740         
14741         this.validate();
14742         
14743         if(this.tickable && !Roo.isTouch){
14744             this.view.refresh();
14745         }
14746     },
14747     
14748     inputEl: function ()
14749     {
14750         if(Roo.isIOS && this.useNativeIOS){
14751             return this.el.select('select.roo-ios-select', true).first();
14752         }
14753         
14754         if(Roo.isTouch && this.mobileTouchView){
14755             return this.el.select('input.form-control',true).first();
14756         }
14757         
14758         if(this.tickable){
14759             return this.searchField;
14760         }
14761         
14762         return this.el.select('input.form-control',true).first();
14763     },
14764     
14765     onTickableFooterButtonClick : function(e, btn, el)
14766     {
14767         e.preventDefault();
14768         
14769         this.lastItem = Roo.apply([], this.item);
14770         
14771         if(btn && btn.name == 'cancel'){
14772             this.tickItems = Roo.apply([], this.item);
14773             this.collapse();
14774             return;
14775         }
14776         
14777         this.clearItem();
14778         
14779         var _this = this;
14780         
14781         Roo.each(this.tickItems, function(o){
14782             _this.addItem(o);
14783         });
14784         
14785         this.collapse();
14786         
14787     },
14788     
14789     validate : function()
14790     {
14791         if(this.getVisibilityEl().hasClass('hidden')){
14792             return true;
14793         }
14794         
14795         var v = this.getRawValue();
14796         
14797         if(this.multiple){
14798             v = this.getValue();
14799         }
14800         
14801         if(this.disabled || this.allowBlank || v.length){
14802             this.markValid();
14803             return true;
14804         }
14805         
14806         this.markInvalid();
14807         return false;
14808     },
14809     
14810     tickableInputEl : function()
14811     {
14812         if(!this.tickable || !this.editable){
14813             return this.inputEl();
14814         }
14815         
14816         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14817     },
14818     
14819     
14820     getAutoCreateTouchView : function()
14821     {
14822         var id = Roo.id();
14823         
14824         var cfg = {
14825             cls: 'form-group' //input-group
14826         };
14827         
14828         var input =  {
14829             tag: 'input',
14830             id : id,
14831             type : this.inputType,
14832             cls : 'form-control x-combo-noedit',
14833             autocomplete: 'new-password',
14834             placeholder : this.placeholder || '',
14835             readonly : true
14836         };
14837         
14838         if (this.name) {
14839             input.name = this.name;
14840         }
14841         
14842         if (this.size) {
14843             input.cls += ' input-' + this.size;
14844         }
14845         
14846         if (this.disabled) {
14847             input.disabled = true;
14848         }
14849         
14850         var inputblock = {
14851             cls : '',
14852             cn : [
14853                 input
14854             ]
14855         };
14856         
14857         if(this.before){
14858             inputblock.cls += ' input-group';
14859             
14860             inputblock.cn.unshift({
14861                 tag :'span',
14862                 cls : 'input-group-addon',
14863                 html : this.before
14864             });
14865         }
14866         
14867         if(this.removable && !this.multiple){
14868             inputblock.cls += ' roo-removable';
14869             
14870             inputblock.cn.push({
14871                 tag: 'button',
14872                 html : 'x',
14873                 cls : 'roo-combo-removable-btn close'
14874             });
14875         }
14876
14877         if(this.hasFeedback && !this.allowBlank){
14878             
14879             inputblock.cls += ' has-feedback';
14880             
14881             inputblock.cn.push({
14882                 tag: 'span',
14883                 cls: 'glyphicon form-control-feedback'
14884             });
14885             
14886         }
14887         
14888         if (this.after) {
14889             
14890             inputblock.cls += (this.before) ? '' : ' input-group';
14891             
14892             inputblock.cn.push({
14893                 tag :'span',
14894                 cls : 'input-group-addon',
14895                 html : this.after
14896             });
14897         }
14898
14899         var box = {
14900             tag: 'div',
14901             cn: [
14902                 {
14903                     tag: 'input',
14904                     type : 'hidden',
14905                     cls: 'form-hidden-field'
14906                 },
14907                 inputblock
14908             ]
14909             
14910         };
14911         
14912         if(this.multiple){
14913             box = {
14914                 tag: 'div',
14915                 cn: [
14916                     {
14917                         tag: 'input',
14918                         type : 'hidden',
14919                         cls: 'form-hidden-field'
14920                     },
14921                     {
14922                         tag: 'ul',
14923                         cls: 'roo-select2-choices',
14924                         cn:[
14925                             {
14926                                 tag: 'li',
14927                                 cls: 'roo-select2-search-field',
14928                                 cn: [
14929
14930                                     inputblock
14931                                 ]
14932                             }
14933                         ]
14934                     }
14935                 ]
14936             }
14937         };
14938         
14939         var combobox = {
14940             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14941             cn: [
14942                 box
14943             ]
14944         };
14945         
14946         if(!this.multiple && this.showToggleBtn){
14947             
14948             var caret = {
14949                         tag: 'span',
14950                         cls: 'caret'
14951             };
14952             
14953             if (this.caret != false) {
14954                 caret = {
14955                      tag: 'i',
14956                      cls: 'fa fa-' + this.caret
14957                 };
14958                 
14959             }
14960             
14961             combobox.cn.push({
14962                 tag :'span',
14963                 cls : 'input-group-addon btn dropdown-toggle',
14964                 cn : [
14965                     caret,
14966                     {
14967                         tag: 'span',
14968                         cls: 'combobox-clear',
14969                         cn  : [
14970                             {
14971                                 tag : 'i',
14972                                 cls: 'icon-remove'
14973                             }
14974                         ]
14975                     }
14976                 ]
14977
14978             })
14979         }
14980         
14981         if(this.multiple){
14982             combobox.cls += ' roo-select2-container-multi';
14983         }
14984         
14985         var align = this.labelAlign || this.parentLabelAlign();
14986         
14987         if (align ==='left' && this.fieldLabel.length) {
14988
14989             cfg.cn = [
14990                 {
14991                    tag : 'i',
14992                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14993                    tooltip : 'This field is required'
14994                 },
14995                 {
14996                     tag: 'label',
14997                     cls : 'control-label',
14998                     html : this.fieldLabel
14999
15000                 },
15001                 {
15002                     cls : '', 
15003                     cn: [
15004                         combobox
15005                     ]
15006                 }
15007             ];
15008             
15009             var labelCfg = cfg.cn[1];
15010             var contentCfg = cfg.cn[2];
15011             
15012
15013             if(this.indicatorpos == 'right'){
15014                 cfg.cn = [
15015                     {
15016                         tag: 'label',
15017                         'for' :  id,
15018                         cls : 'control-label',
15019                         cn : [
15020                             {
15021                                 tag : 'span',
15022                                 html : this.fieldLabel
15023                             },
15024                             {
15025                                 tag : 'i',
15026                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15027                                 tooltip : 'This field is required'
15028                             }
15029                         ]
15030                     },
15031                     {
15032                         cls : "",
15033                         cn: [
15034                             combobox
15035                         ]
15036                     }
15037
15038                 ];
15039                 
15040                 labelCfg = cfg.cn[0];
15041                 contentCfg = cfg.cn[1];
15042             }
15043             
15044            
15045             
15046             if(this.labelWidth > 12){
15047                 labelCfg.style = "width: " + this.labelWidth + 'px';
15048             }
15049             
15050             if(this.labelWidth < 13 && this.labelmd == 0){
15051                 this.labelmd = this.labelWidth;
15052             }
15053             
15054             if(this.labellg > 0){
15055                 labelCfg.cls += ' col-lg-' + this.labellg;
15056                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15057             }
15058             
15059             if(this.labelmd > 0){
15060                 labelCfg.cls += ' col-md-' + this.labelmd;
15061                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15062             }
15063             
15064             if(this.labelsm > 0){
15065                 labelCfg.cls += ' col-sm-' + this.labelsm;
15066                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15067             }
15068             
15069             if(this.labelxs > 0){
15070                 labelCfg.cls += ' col-xs-' + this.labelxs;
15071                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15072             }
15073                 
15074                 
15075         } else if ( this.fieldLabel.length) {
15076             cfg.cn = [
15077                 {
15078                    tag : 'i',
15079                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15080                    tooltip : 'This field is required'
15081                 },
15082                 {
15083                     tag: 'label',
15084                     cls : 'control-label',
15085                     html : this.fieldLabel
15086
15087                 },
15088                 {
15089                     cls : '', 
15090                     cn: [
15091                         combobox
15092                     ]
15093                 }
15094             ];
15095             
15096             if(this.indicatorpos == 'right'){
15097                 cfg.cn = [
15098                     {
15099                         tag: 'label',
15100                         cls : 'control-label',
15101                         html : this.fieldLabel,
15102                         cn : [
15103                             {
15104                                tag : 'i',
15105                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15106                                tooltip : 'This field is required'
15107                             }
15108                         ]
15109                     },
15110                     {
15111                         cls : '', 
15112                         cn: [
15113                             combobox
15114                         ]
15115                     }
15116                 ];
15117             }
15118         } else {
15119             cfg.cn = combobox;    
15120         }
15121         
15122         
15123         var settings = this;
15124         
15125         ['xs','sm','md','lg'].map(function(size){
15126             if (settings[size]) {
15127                 cfg.cls += ' col-' + size + '-' + settings[size];
15128             }
15129         });
15130         
15131         return cfg;
15132     },
15133     
15134     initTouchView : function()
15135     {
15136         this.renderTouchView();
15137         
15138         this.touchViewEl.on('scroll', function(){
15139             this.el.dom.scrollTop = 0;
15140         }, this);
15141         
15142         this.originalValue = this.getValue();
15143         
15144         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15145         
15146         this.inputEl().on("click", this.showTouchView, this);
15147         if (this.triggerEl) {
15148             this.triggerEl.on("click", this.showTouchView, this);
15149         }
15150         
15151         
15152         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15153         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15154         
15155         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15156         
15157         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15158         this.store.on('load', this.onTouchViewLoad, this);
15159         this.store.on('loadexception', this.onTouchViewLoadException, this);
15160         
15161         if(this.hiddenName){
15162             
15163             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15164             
15165             this.hiddenField.dom.value =
15166                 this.hiddenValue !== undefined ? this.hiddenValue :
15167                 this.value !== undefined ? this.value : '';
15168         
15169             this.el.dom.removeAttribute('name');
15170             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15171         }
15172         
15173         if(this.multiple){
15174             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15175             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15176         }
15177         
15178         if(this.removable && !this.multiple){
15179             var close = this.closeTriggerEl();
15180             if(close){
15181                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15182                 close.on('click', this.removeBtnClick, this, close);
15183             }
15184         }
15185         /*
15186          * fix the bug in Safari iOS8
15187          */
15188         this.inputEl().on("focus", function(e){
15189             document.activeElement.blur();
15190         }, this);
15191         
15192         return;
15193         
15194         
15195     },
15196     
15197     renderTouchView : function()
15198     {
15199         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15200         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15201         
15202         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15203         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15204         
15205         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15206         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15207         this.touchViewBodyEl.setStyle('overflow', 'auto');
15208         
15209         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15210         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15211         
15212         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15213         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15214         
15215     },
15216     
15217     showTouchView : function()
15218     {
15219         if(this.disabled){
15220             return;
15221         }
15222         
15223         this.touchViewHeaderEl.hide();
15224
15225         if(this.modalTitle.length){
15226             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15227             this.touchViewHeaderEl.show();
15228         }
15229
15230         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15231         this.touchViewEl.show();
15232
15233         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15234         
15235         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15236         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15237
15238         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15239
15240         if(this.modalTitle.length){
15241             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15242         }
15243         
15244         this.touchViewBodyEl.setHeight(bodyHeight);
15245
15246         if(this.animate){
15247             var _this = this;
15248             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15249         }else{
15250             this.touchViewEl.addClass('in');
15251         }
15252
15253         this.doTouchViewQuery();
15254         
15255     },
15256     
15257     hideTouchView : function()
15258     {
15259         this.touchViewEl.removeClass('in');
15260
15261         if(this.animate){
15262             var _this = this;
15263             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15264         }else{
15265             this.touchViewEl.setStyle('display', 'none');
15266         }
15267         
15268     },
15269     
15270     setTouchViewValue : function()
15271     {
15272         if(this.multiple){
15273             this.clearItem();
15274         
15275             var _this = this;
15276
15277             Roo.each(this.tickItems, function(o){
15278                 this.addItem(o);
15279             }, this);
15280         }
15281         
15282         this.hideTouchView();
15283     },
15284     
15285     doTouchViewQuery : function()
15286     {
15287         var qe = {
15288             query: '',
15289             forceAll: true,
15290             combo: this,
15291             cancel:false
15292         };
15293         
15294         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15295             return false;
15296         }
15297         
15298         if(!this.alwaysQuery || this.mode == 'local'){
15299             this.onTouchViewLoad();
15300             return;
15301         }
15302         
15303         this.store.load();
15304     },
15305     
15306     onTouchViewBeforeLoad : function(combo,opts)
15307     {
15308         return;
15309     },
15310
15311     // private
15312     onTouchViewLoad : function()
15313     {
15314         if(this.store.getCount() < 1){
15315             this.onTouchViewEmptyResults();
15316             return;
15317         }
15318         
15319         this.clearTouchView();
15320         
15321         var rawValue = this.getRawValue();
15322         
15323         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15324         
15325         this.tickItems = [];
15326         
15327         this.store.data.each(function(d, rowIndex){
15328             var row = this.touchViewListGroup.createChild(template);
15329             
15330             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15331                 row.addClass(d.data.cls);
15332             }
15333             
15334             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15335                 var cfg = {
15336                     data : d.data,
15337                     html : d.data[this.displayField]
15338                 };
15339                 
15340                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15341                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15342                 }
15343             }
15344             row.removeClass('selected');
15345             if(!this.multiple && this.valueField &&
15346                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15347             {
15348                 // radio buttons..
15349                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15350                 row.addClass('selected');
15351             }
15352             
15353             if(this.multiple && this.valueField &&
15354                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15355             {
15356                 
15357                 // checkboxes...
15358                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15359                 this.tickItems.push(d.data);
15360             }
15361             
15362             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15363             
15364         }, this);
15365         
15366         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15367         
15368         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15369
15370         if(this.modalTitle.length){
15371             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15372         }
15373
15374         var listHeight = this.touchViewListGroup.getHeight();
15375         
15376         var _this = this;
15377         
15378         if(firstChecked && listHeight > bodyHeight){
15379             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15380         }
15381         
15382     },
15383     
15384     onTouchViewLoadException : function()
15385     {
15386         this.hideTouchView();
15387     },
15388     
15389     onTouchViewEmptyResults : function()
15390     {
15391         this.clearTouchView();
15392         
15393         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15394         
15395         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15396         
15397     },
15398     
15399     clearTouchView : function()
15400     {
15401         this.touchViewListGroup.dom.innerHTML = '';
15402     },
15403     
15404     onTouchViewClick : function(e, el, o)
15405     {
15406         e.preventDefault();
15407         
15408         var row = o.row;
15409         var rowIndex = o.rowIndex;
15410         
15411         var r = this.store.getAt(rowIndex);
15412         
15413         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15414             
15415             if(!this.multiple){
15416                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15417                     c.dom.removeAttribute('checked');
15418                 }, this);
15419
15420                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15421
15422                 this.setFromData(r.data);
15423
15424                 var close = this.closeTriggerEl();
15425
15426                 if(close){
15427                     close.show();
15428                 }
15429
15430                 this.hideTouchView();
15431
15432                 this.fireEvent('select', this, r, rowIndex);
15433
15434                 return;
15435             }
15436
15437             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15438                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15439                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15440                 return;
15441             }
15442
15443             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15444             this.addItem(r.data);
15445             this.tickItems.push(r.data);
15446         }
15447     },
15448     
15449     getAutoCreateNativeIOS : function()
15450     {
15451         var cfg = {
15452             cls: 'form-group' //input-group,
15453         };
15454         
15455         var combobox =  {
15456             tag: 'select',
15457             cls : 'roo-ios-select'
15458         };
15459         
15460         if (this.name) {
15461             combobox.name = this.name;
15462         }
15463         
15464         if (this.disabled) {
15465             combobox.disabled = true;
15466         }
15467         
15468         var settings = this;
15469         
15470         ['xs','sm','md','lg'].map(function(size){
15471             if (settings[size]) {
15472                 cfg.cls += ' col-' + size + '-' + settings[size];
15473             }
15474         });
15475         
15476         cfg.cn = combobox;
15477         
15478         return cfg;
15479         
15480     },
15481     
15482     initIOSView : function()
15483     {
15484         this.store.on('load', this.onIOSViewLoad, this);
15485         
15486         return;
15487     },
15488     
15489     onIOSViewLoad : function()
15490     {
15491         if(this.store.getCount() < 1){
15492             return;
15493         }
15494         
15495         this.clearIOSView();
15496         
15497         if(this.allowBlank) {
15498             
15499             var default_text = '-- SELECT --';
15500             
15501             if(this.placeholder.length){
15502                 default_text = this.placeholder;
15503             }
15504             
15505             if(this.emptyTitle.length){
15506                 default_text += ' - ' + this.emptyTitle + ' -';
15507             }
15508             
15509             var opt = this.inputEl().createChild({
15510                 tag: 'option',
15511                 value : 0,
15512                 html : default_text
15513             });
15514             
15515             var o = {};
15516             o[this.valueField] = 0;
15517             o[this.displayField] = default_text;
15518             
15519             this.ios_options.push({
15520                 data : o,
15521                 el : opt
15522             });
15523             
15524         }
15525         
15526         this.store.data.each(function(d, rowIndex){
15527             
15528             var html = '';
15529             
15530             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15531                 html = d.data[this.displayField];
15532             }
15533             
15534             var value = '';
15535             
15536             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15537                 value = d.data[this.valueField];
15538             }
15539             
15540             var option = {
15541                 tag: 'option',
15542                 value : value,
15543                 html : html
15544             };
15545             
15546             if(this.value == d.data[this.valueField]){
15547                 option['selected'] = true;
15548             }
15549             
15550             var opt = this.inputEl().createChild(option);
15551             
15552             this.ios_options.push({
15553                 data : d.data,
15554                 el : opt
15555             });
15556             
15557         }, this);
15558         
15559         this.inputEl().on('change', function(){
15560            this.fireEvent('select', this);
15561         }, this);
15562         
15563     },
15564     
15565     clearIOSView: function()
15566     {
15567         this.inputEl().dom.innerHTML = '';
15568         
15569         this.ios_options = [];
15570     },
15571     
15572     setIOSValue: function(v)
15573     {
15574         this.value = v;
15575         
15576         if(!this.ios_options){
15577             return;
15578         }
15579         
15580         Roo.each(this.ios_options, function(opts){
15581            
15582            opts.el.dom.removeAttribute('selected');
15583            
15584            if(opts.data[this.valueField] != v){
15585                return;
15586            }
15587            
15588            opts.el.dom.setAttribute('selected', true);
15589            
15590         }, this);
15591     }
15592
15593     /** 
15594     * @cfg {Boolean} grow 
15595     * @hide 
15596     */
15597     /** 
15598     * @cfg {Number} growMin 
15599     * @hide 
15600     */
15601     /** 
15602     * @cfg {Number} growMax 
15603     * @hide 
15604     */
15605     /**
15606      * @hide
15607      * @method autoSize
15608      */
15609 });
15610
15611 Roo.apply(Roo.bootstrap.ComboBox,  {
15612     
15613     header : {
15614         tag: 'div',
15615         cls: 'modal-header',
15616         cn: [
15617             {
15618                 tag: 'h4',
15619                 cls: 'modal-title'
15620             }
15621         ]
15622     },
15623     
15624     body : {
15625         tag: 'div',
15626         cls: 'modal-body',
15627         cn: [
15628             {
15629                 tag: 'ul',
15630                 cls: 'list-group'
15631             }
15632         ]
15633     },
15634     
15635     listItemRadio : {
15636         tag: 'li',
15637         cls: 'list-group-item',
15638         cn: [
15639             {
15640                 tag: 'span',
15641                 cls: 'roo-combobox-list-group-item-value'
15642             },
15643             {
15644                 tag: 'div',
15645                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15646                 cn: [
15647                     {
15648                         tag: 'input',
15649                         type: 'radio'
15650                     },
15651                     {
15652                         tag: 'label'
15653                     }
15654                 ]
15655             }
15656         ]
15657     },
15658     
15659     listItemCheckbox : {
15660         tag: 'li',
15661         cls: 'list-group-item',
15662         cn: [
15663             {
15664                 tag: 'span',
15665                 cls: 'roo-combobox-list-group-item-value'
15666             },
15667             {
15668                 tag: 'div',
15669                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15670                 cn: [
15671                     {
15672                         tag: 'input',
15673                         type: 'checkbox'
15674                     },
15675                     {
15676                         tag: 'label'
15677                     }
15678                 ]
15679             }
15680         ]
15681     },
15682     
15683     emptyResult : {
15684         tag: 'div',
15685         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15686     },
15687     
15688     footer : {
15689         tag: 'div',
15690         cls: 'modal-footer',
15691         cn: [
15692             {
15693                 tag: 'div',
15694                 cls: 'row',
15695                 cn: [
15696                     {
15697                         tag: 'div',
15698                         cls: 'col-xs-6 text-left',
15699                         cn: {
15700                             tag: 'button',
15701                             cls: 'btn btn-danger roo-touch-view-cancel',
15702                             html: 'Cancel'
15703                         }
15704                     },
15705                     {
15706                         tag: 'div',
15707                         cls: 'col-xs-6 text-right',
15708                         cn: {
15709                             tag: 'button',
15710                             cls: 'btn btn-success roo-touch-view-ok',
15711                             html: 'OK'
15712                         }
15713                     }
15714                 ]
15715             }
15716         ]
15717         
15718     }
15719 });
15720
15721 Roo.apply(Roo.bootstrap.ComboBox,  {
15722     
15723     touchViewTemplate : {
15724         tag: 'div',
15725         cls: 'modal fade roo-combobox-touch-view',
15726         cn: [
15727             {
15728                 tag: 'div',
15729                 cls: 'modal-dialog',
15730                 style : 'position:fixed', // we have to fix position....
15731                 cn: [
15732                     {
15733                         tag: 'div',
15734                         cls: 'modal-content',
15735                         cn: [
15736                             Roo.bootstrap.ComboBox.header,
15737                             Roo.bootstrap.ComboBox.body,
15738                             Roo.bootstrap.ComboBox.footer
15739                         ]
15740                     }
15741                 ]
15742             }
15743         ]
15744     }
15745 });/*
15746  * Based on:
15747  * Ext JS Library 1.1.1
15748  * Copyright(c) 2006-2007, Ext JS, LLC.
15749  *
15750  * Originally Released Under LGPL - original licence link has changed is not relivant.
15751  *
15752  * Fork - LGPL
15753  * <script type="text/javascript">
15754  */
15755
15756 /**
15757  * @class Roo.View
15758  * @extends Roo.util.Observable
15759  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15760  * This class also supports single and multi selection modes. <br>
15761  * Create a data model bound view:
15762  <pre><code>
15763  var store = new Roo.data.Store(...);
15764
15765  var view = new Roo.View({
15766     el : "my-element",
15767     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15768  
15769     singleSelect: true,
15770     selectedClass: "ydataview-selected",
15771     store: store
15772  });
15773
15774  // listen for node click?
15775  view.on("click", function(vw, index, node, e){
15776  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15777  });
15778
15779  // load XML data
15780  dataModel.load("foobar.xml");
15781  </code></pre>
15782  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15783  * <br><br>
15784  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15785  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15786  * 
15787  * Note: old style constructor is still suported (container, template, config)
15788  * 
15789  * @constructor
15790  * Create a new View
15791  * @param {Object} config The config object
15792  * 
15793  */
15794 Roo.View = function(config, depreciated_tpl, depreciated_config){
15795     
15796     this.parent = false;
15797     
15798     if (typeof(depreciated_tpl) == 'undefined') {
15799         // new way.. - universal constructor.
15800         Roo.apply(this, config);
15801         this.el  = Roo.get(this.el);
15802     } else {
15803         // old format..
15804         this.el  = Roo.get(config);
15805         this.tpl = depreciated_tpl;
15806         Roo.apply(this, depreciated_config);
15807     }
15808     this.wrapEl  = this.el.wrap().wrap();
15809     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15810     
15811     
15812     if(typeof(this.tpl) == "string"){
15813         this.tpl = new Roo.Template(this.tpl);
15814     } else {
15815         // support xtype ctors..
15816         this.tpl = new Roo.factory(this.tpl, Roo);
15817     }
15818     
15819     
15820     this.tpl.compile();
15821     
15822     /** @private */
15823     this.addEvents({
15824         /**
15825          * @event beforeclick
15826          * Fires before a click is processed. Returns false to cancel the default action.
15827          * @param {Roo.View} this
15828          * @param {Number} index The index of the target node
15829          * @param {HTMLElement} node The target node
15830          * @param {Roo.EventObject} e The raw event object
15831          */
15832             "beforeclick" : true,
15833         /**
15834          * @event click
15835          * Fires when a template node is clicked.
15836          * @param {Roo.View} this
15837          * @param {Number} index The index of the target node
15838          * @param {HTMLElement} node The target node
15839          * @param {Roo.EventObject} e The raw event object
15840          */
15841             "click" : true,
15842         /**
15843          * @event dblclick
15844          * Fires when a template node is double clicked.
15845          * @param {Roo.View} this
15846          * @param {Number} index The index of the target node
15847          * @param {HTMLElement} node The target node
15848          * @param {Roo.EventObject} e The raw event object
15849          */
15850             "dblclick" : true,
15851         /**
15852          * @event contextmenu
15853          * Fires when a template node is right clicked.
15854          * @param {Roo.View} this
15855          * @param {Number} index The index of the target node
15856          * @param {HTMLElement} node The target node
15857          * @param {Roo.EventObject} e The raw event object
15858          */
15859             "contextmenu" : true,
15860         /**
15861          * @event selectionchange
15862          * Fires when the selected nodes change.
15863          * @param {Roo.View} this
15864          * @param {Array} selections Array of the selected nodes
15865          */
15866             "selectionchange" : true,
15867     
15868         /**
15869          * @event beforeselect
15870          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15871          * @param {Roo.View} this
15872          * @param {HTMLElement} node The node to be selected
15873          * @param {Array} selections Array of currently selected nodes
15874          */
15875             "beforeselect" : true,
15876         /**
15877          * @event preparedata
15878          * Fires on every row to render, to allow you to change the data.
15879          * @param {Roo.View} this
15880          * @param {Object} data to be rendered (change this)
15881          */
15882           "preparedata" : true
15883           
15884           
15885         });
15886
15887
15888
15889     this.el.on({
15890         "click": this.onClick,
15891         "dblclick": this.onDblClick,
15892         "contextmenu": this.onContextMenu,
15893         scope:this
15894     });
15895
15896     this.selections = [];
15897     this.nodes = [];
15898     this.cmp = new Roo.CompositeElementLite([]);
15899     if(this.store){
15900         this.store = Roo.factory(this.store, Roo.data);
15901         this.setStore(this.store, true);
15902     }
15903     
15904     if ( this.footer && this.footer.xtype) {
15905            
15906          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15907         
15908         this.footer.dataSource = this.store;
15909         this.footer.container = fctr;
15910         this.footer = Roo.factory(this.footer, Roo);
15911         fctr.insertFirst(this.el);
15912         
15913         // this is a bit insane - as the paging toolbar seems to detach the el..
15914 //        dom.parentNode.parentNode.parentNode
15915          // they get detached?
15916     }
15917     
15918     
15919     Roo.View.superclass.constructor.call(this);
15920     
15921     
15922 };
15923
15924 Roo.extend(Roo.View, Roo.util.Observable, {
15925     
15926      /**
15927      * @cfg {Roo.data.Store} store Data store to load data from.
15928      */
15929     store : false,
15930     
15931     /**
15932      * @cfg {String|Roo.Element} el The container element.
15933      */
15934     el : '',
15935     
15936     /**
15937      * @cfg {String|Roo.Template} tpl The template used by this View 
15938      */
15939     tpl : false,
15940     /**
15941      * @cfg {String} dataName the named area of the template to use as the data area
15942      *                          Works with domtemplates roo-name="name"
15943      */
15944     dataName: false,
15945     /**
15946      * @cfg {String} selectedClass The css class to add to selected nodes
15947      */
15948     selectedClass : "x-view-selected",
15949      /**
15950      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15951      */
15952     emptyText : "",
15953     
15954     /**
15955      * @cfg {String} text to display on mask (default Loading)
15956      */
15957     mask : false,
15958     /**
15959      * @cfg {Boolean} multiSelect Allow multiple selection
15960      */
15961     multiSelect : false,
15962     /**
15963      * @cfg {Boolean} singleSelect Allow single selection
15964      */
15965     singleSelect:  false,
15966     
15967     /**
15968      * @cfg {Boolean} toggleSelect - selecting 
15969      */
15970     toggleSelect : false,
15971     
15972     /**
15973      * @cfg {Boolean} tickable - selecting 
15974      */
15975     tickable : false,
15976     
15977     /**
15978      * Returns the element this view is bound to.
15979      * @return {Roo.Element}
15980      */
15981     getEl : function(){
15982         return this.wrapEl;
15983     },
15984     
15985     
15986
15987     /**
15988      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15989      */
15990     refresh : function(){
15991         //Roo.log('refresh');
15992         var t = this.tpl;
15993         
15994         // if we are using something like 'domtemplate', then
15995         // the what gets used is:
15996         // t.applySubtemplate(NAME, data, wrapping data..)
15997         // the outer template then get' applied with
15998         //     the store 'extra data'
15999         // and the body get's added to the
16000         //      roo-name="data" node?
16001         //      <span class='roo-tpl-{name}'></span> ?????
16002         
16003         
16004         
16005         this.clearSelections();
16006         this.el.update("");
16007         var html = [];
16008         var records = this.store.getRange();
16009         if(records.length < 1) {
16010             
16011             // is this valid??  = should it render a template??
16012             
16013             this.el.update(this.emptyText);
16014             return;
16015         }
16016         var el = this.el;
16017         if (this.dataName) {
16018             this.el.update(t.apply(this.store.meta)); //????
16019             el = this.el.child('.roo-tpl-' + this.dataName);
16020         }
16021         
16022         for(var i = 0, len = records.length; i < len; i++){
16023             var data = this.prepareData(records[i].data, i, records[i]);
16024             this.fireEvent("preparedata", this, data, i, records[i]);
16025             
16026             var d = Roo.apply({}, data);
16027             
16028             if(this.tickable){
16029                 Roo.apply(d, {'roo-id' : Roo.id()});
16030                 
16031                 var _this = this;
16032             
16033                 Roo.each(this.parent.item, function(item){
16034                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16035                         return;
16036                     }
16037                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16038                 });
16039             }
16040             
16041             html[html.length] = Roo.util.Format.trim(
16042                 this.dataName ?
16043                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16044                     t.apply(d)
16045             );
16046         }
16047         
16048         
16049         
16050         el.update(html.join(""));
16051         this.nodes = el.dom.childNodes;
16052         this.updateIndexes(0);
16053     },
16054     
16055
16056     /**
16057      * Function to override to reformat the data that is sent to
16058      * the template for each node.
16059      * DEPRICATED - use the preparedata event handler.
16060      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16061      * a JSON object for an UpdateManager bound view).
16062      */
16063     prepareData : function(data, index, record)
16064     {
16065         this.fireEvent("preparedata", this, data, index, record);
16066         return data;
16067     },
16068
16069     onUpdate : function(ds, record){
16070         // Roo.log('on update');   
16071         this.clearSelections();
16072         var index = this.store.indexOf(record);
16073         var n = this.nodes[index];
16074         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16075         n.parentNode.removeChild(n);
16076         this.updateIndexes(index, index);
16077     },
16078
16079     
16080     
16081 // --------- FIXME     
16082     onAdd : function(ds, records, index)
16083     {
16084         //Roo.log(['on Add', ds, records, index] );        
16085         this.clearSelections();
16086         if(this.nodes.length == 0){
16087             this.refresh();
16088             return;
16089         }
16090         var n = this.nodes[index];
16091         for(var i = 0, len = records.length; i < len; i++){
16092             var d = this.prepareData(records[i].data, i, records[i]);
16093             if(n){
16094                 this.tpl.insertBefore(n, d);
16095             }else{
16096                 
16097                 this.tpl.append(this.el, d);
16098             }
16099         }
16100         this.updateIndexes(index);
16101     },
16102
16103     onRemove : function(ds, record, index){
16104        // Roo.log('onRemove');
16105         this.clearSelections();
16106         var el = this.dataName  ?
16107             this.el.child('.roo-tpl-' + this.dataName) :
16108             this.el; 
16109         
16110         el.dom.removeChild(this.nodes[index]);
16111         this.updateIndexes(index);
16112     },
16113
16114     /**
16115      * Refresh an individual node.
16116      * @param {Number} index
16117      */
16118     refreshNode : function(index){
16119         this.onUpdate(this.store, this.store.getAt(index));
16120     },
16121
16122     updateIndexes : function(startIndex, endIndex){
16123         var ns = this.nodes;
16124         startIndex = startIndex || 0;
16125         endIndex = endIndex || ns.length - 1;
16126         for(var i = startIndex; i <= endIndex; i++){
16127             ns[i].nodeIndex = i;
16128         }
16129     },
16130
16131     /**
16132      * Changes the data store this view uses and refresh the view.
16133      * @param {Store} store
16134      */
16135     setStore : function(store, initial){
16136         if(!initial && this.store){
16137             this.store.un("datachanged", this.refresh);
16138             this.store.un("add", this.onAdd);
16139             this.store.un("remove", this.onRemove);
16140             this.store.un("update", this.onUpdate);
16141             this.store.un("clear", this.refresh);
16142             this.store.un("beforeload", this.onBeforeLoad);
16143             this.store.un("load", this.onLoad);
16144             this.store.un("loadexception", this.onLoad);
16145         }
16146         if(store){
16147           
16148             store.on("datachanged", this.refresh, this);
16149             store.on("add", this.onAdd, this);
16150             store.on("remove", this.onRemove, this);
16151             store.on("update", this.onUpdate, this);
16152             store.on("clear", this.refresh, this);
16153             store.on("beforeload", this.onBeforeLoad, this);
16154             store.on("load", this.onLoad, this);
16155             store.on("loadexception", this.onLoad, this);
16156         }
16157         
16158         if(store){
16159             this.refresh();
16160         }
16161     },
16162     /**
16163      * onbeforeLoad - masks the loading area.
16164      *
16165      */
16166     onBeforeLoad : function(store,opts)
16167     {
16168          //Roo.log('onBeforeLoad');   
16169         if (!opts.add) {
16170             this.el.update("");
16171         }
16172         this.el.mask(this.mask ? this.mask : "Loading" ); 
16173     },
16174     onLoad : function ()
16175     {
16176         this.el.unmask();
16177     },
16178     
16179
16180     /**
16181      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16182      * @param {HTMLElement} node
16183      * @return {HTMLElement} The template node
16184      */
16185     findItemFromChild : function(node){
16186         var el = this.dataName  ?
16187             this.el.child('.roo-tpl-' + this.dataName,true) :
16188             this.el.dom; 
16189         
16190         if(!node || node.parentNode == el){
16191                     return node;
16192             }
16193             var p = node.parentNode;
16194             while(p && p != el){
16195             if(p.parentNode == el){
16196                 return p;
16197             }
16198             p = p.parentNode;
16199         }
16200             return null;
16201     },
16202
16203     /** @ignore */
16204     onClick : function(e){
16205         var item = this.findItemFromChild(e.getTarget());
16206         if(item){
16207             var index = this.indexOf(item);
16208             if(this.onItemClick(item, index, e) !== false){
16209                 this.fireEvent("click", this, index, item, e);
16210             }
16211         }else{
16212             this.clearSelections();
16213         }
16214     },
16215
16216     /** @ignore */
16217     onContextMenu : function(e){
16218         var item = this.findItemFromChild(e.getTarget());
16219         if(item){
16220             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16221         }
16222     },
16223
16224     /** @ignore */
16225     onDblClick : function(e){
16226         var item = this.findItemFromChild(e.getTarget());
16227         if(item){
16228             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16229         }
16230     },
16231
16232     onItemClick : function(item, index, e)
16233     {
16234         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16235             return false;
16236         }
16237         if (this.toggleSelect) {
16238             var m = this.isSelected(item) ? 'unselect' : 'select';
16239             //Roo.log(m);
16240             var _t = this;
16241             _t[m](item, true, false);
16242             return true;
16243         }
16244         if(this.multiSelect || this.singleSelect){
16245             if(this.multiSelect && e.shiftKey && this.lastSelection){
16246                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16247             }else{
16248                 this.select(item, this.multiSelect && e.ctrlKey);
16249                 this.lastSelection = item;
16250             }
16251             
16252             if(!this.tickable){
16253                 e.preventDefault();
16254             }
16255             
16256         }
16257         return true;
16258     },
16259
16260     /**
16261      * Get the number of selected nodes.
16262      * @return {Number}
16263      */
16264     getSelectionCount : function(){
16265         return this.selections.length;
16266     },
16267
16268     /**
16269      * Get the currently selected nodes.
16270      * @return {Array} An array of HTMLElements
16271      */
16272     getSelectedNodes : function(){
16273         return this.selections;
16274     },
16275
16276     /**
16277      * Get the indexes of the selected nodes.
16278      * @return {Array}
16279      */
16280     getSelectedIndexes : function(){
16281         var indexes = [], s = this.selections;
16282         for(var i = 0, len = s.length; i < len; i++){
16283             indexes.push(s[i].nodeIndex);
16284         }
16285         return indexes;
16286     },
16287
16288     /**
16289      * Clear all selections
16290      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16291      */
16292     clearSelections : function(suppressEvent){
16293         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16294             this.cmp.elements = this.selections;
16295             this.cmp.removeClass(this.selectedClass);
16296             this.selections = [];
16297             if(!suppressEvent){
16298                 this.fireEvent("selectionchange", this, this.selections);
16299             }
16300         }
16301     },
16302
16303     /**
16304      * Returns true if the passed node is selected
16305      * @param {HTMLElement/Number} node The node or node index
16306      * @return {Boolean}
16307      */
16308     isSelected : function(node){
16309         var s = this.selections;
16310         if(s.length < 1){
16311             return false;
16312         }
16313         node = this.getNode(node);
16314         return s.indexOf(node) !== -1;
16315     },
16316
16317     /**
16318      * Selects nodes.
16319      * @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
16320      * @param {Boolean} keepExisting (optional) true to keep existing selections
16321      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16322      */
16323     select : function(nodeInfo, keepExisting, suppressEvent){
16324         if(nodeInfo instanceof Array){
16325             if(!keepExisting){
16326                 this.clearSelections(true);
16327             }
16328             for(var i = 0, len = nodeInfo.length; i < len; i++){
16329                 this.select(nodeInfo[i], true, true);
16330             }
16331             return;
16332         } 
16333         var node = this.getNode(nodeInfo);
16334         if(!node || this.isSelected(node)){
16335             return; // already selected.
16336         }
16337         if(!keepExisting){
16338             this.clearSelections(true);
16339         }
16340         
16341         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16342             Roo.fly(node).addClass(this.selectedClass);
16343             this.selections.push(node);
16344             if(!suppressEvent){
16345                 this.fireEvent("selectionchange", this, this.selections);
16346             }
16347         }
16348         
16349         
16350     },
16351       /**
16352      * Unselects nodes.
16353      * @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
16354      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16355      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16356      */
16357     unselect : function(nodeInfo, keepExisting, suppressEvent)
16358     {
16359         if(nodeInfo instanceof Array){
16360             Roo.each(this.selections, function(s) {
16361                 this.unselect(s, nodeInfo);
16362             }, this);
16363             return;
16364         }
16365         var node = this.getNode(nodeInfo);
16366         if(!node || !this.isSelected(node)){
16367             //Roo.log("not selected");
16368             return; // not selected.
16369         }
16370         // fireevent???
16371         var ns = [];
16372         Roo.each(this.selections, function(s) {
16373             if (s == node ) {
16374                 Roo.fly(node).removeClass(this.selectedClass);
16375
16376                 return;
16377             }
16378             ns.push(s);
16379         },this);
16380         
16381         this.selections= ns;
16382         this.fireEvent("selectionchange", this, this.selections);
16383     },
16384
16385     /**
16386      * Gets a template node.
16387      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16388      * @return {HTMLElement} The node or null if it wasn't found
16389      */
16390     getNode : function(nodeInfo){
16391         if(typeof nodeInfo == "string"){
16392             return document.getElementById(nodeInfo);
16393         }else if(typeof nodeInfo == "number"){
16394             return this.nodes[nodeInfo];
16395         }
16396         return nodeInfo;
16397     },
16398
16399     /**
16400      * Gets a range template nodes.
16401      * @param {Number} startIndex
16402      * @param {Number} endIndex
16403      * @return {Array} An array of nodes
16404      */
16405     getNodes : function(start, end){
16406         var ns = this.nodes;
16407         start = start || 0;
16408         end = typeof end == "undefined" ? ns.length - 1 : end;
16409         var nodes = [];
16410         if(start <= end){
16411             for(var i = start; i <= end; i++){
16412                 nodes.push(ns[i]);
16413             }
16414         } else{
16415             for(var i = start; i >= end; i--){
16416                 nodes.push(ns[i]);
16417             }
16418         }
16419         return nodes;
16420     },
16421
16422     /**
16423      * Finds the index of the passed node
16424      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16425      * @return {Number} The index of the node or -1
16426      */
16427     indexOf : function(node){
16428         node = this.getNode(node);
16429         if(typeof node.nodeIndex == "number"){
16430             return node.nodeIndex;
16431         }
16432         var ns = this.nodes;
16433         for(var i = 0, len = ns.length; i < len; i++){
16434             if(ns[i] == node){
16435                 return i;
16436             }
16437         }
16438         return -1;
16439     }
16440 });
16441 /*
16442  * - LGPL
16443  *
16444  * based on jquery fullcalendar
16445  * 
16446  */
16447
16448 Roo.bootstrap = Roo.bootstrap || {};
16449 /**
16450  * @class Roo.bootstrap.Calendar
16451  * @extends Roo.bootstrap.Component
16452  * Bootstrap Calendar class
16453  * @cfg {Boolean} loadMask (true|false) default false
16454  * @cfg {Object} header generate the user specific header of the calendar, default false
16455
16456  * @constructor
16457  * Create a new Container
16458  * @param {Object} config The config object
16459  */
16460
16461
16462
16463 Roo.bootstrap.Calendar = function(config){
16464     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16465      this.addEvents({
16466         /**
16467              * @event select
16468              * Fires when a date is selected
16469              * @param {DatePicker} this
16470              * @param {Date} date The selected date
16471              */
16472         'select': true,
16473         /**
16474              * @event monthchange
16475              * Fires when the displayed month changes 
16476              * @param {DatePicker} this
16477              * @param {Date} date The selected month
16478              */
16479         'monthchange': true,
16480         /**
16481              * @event evententer
16482              * Fires when mouse over an event
16483              * @param {Calendar} this
16484              * @param {event} Event
16485              */
16486         'evententer': true,
16487         /**
16488              * @event eventleave
16489              * Fires when the mouse leaves an
16490              * @param {Calendar} this
16491              * @param {event}
16492              */
16493         'eventleave': true,
16494         /**
16495              * @event eventclick
16496              * Fires when the mouse click an
16497              * @param {Calendar} this
16498              * @param {event}
16499              */
16500         'eventclick': true
16501         
16502     });
16503
16504 };
16505
16506 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16507     
16508      /**
16509      * @cfg {Number} startDay
16510      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16511      */
16512     startDay : 0,
16513     
16514     loadMask : false,
16515     
16516     header : false,
16517       
16518     getAutoCreate : function(){
16519         
16520         
16521         var fc_button = function(name, corner, style, content ) {
16522             return Roo.apply({},{
16523                 tag : 'span',
16524                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16525                          (corner.length ?
16526                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16527                             ''
16528                         ),
16529                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16530                 unselectable: 'on'
16531             });
16532         };
16533         
16534         var header = {};
16535         
16536         if(!this.header){
16537             header = {
16538                 tag : 'table',
16539                 cls : 'fc-header',
16540                 style : 'width:100%',
16541                 cn : [
16542                     {
16543                         tag: 'tr',
16544                         cn : [
16545                             {
16546                                 tag : 'td',
16547                                 cls : 'fc-header-left',
16548                                 cn : [
16549                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16550                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16551                                     { tag: 'span', cls: 'fc-header-space' },
16552                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16553
16554
16555                                 ]
16556                             },
16557
16558                             {
16559                                 tag : 'td',
16560                                 cls : 'fc-header-center',
16561                                 cn : [
16562                                     {
16563                                         tag: 'span',
16564                                         cls: 'fc-header-title',
16565                                         cn : {
16566                                             tag: 'H2',
16567                                             html : 'month / year'
16568                                         }
16569                                     }
16570
16571                                 ]
16572                             },
16573                             {
16574                                 tag : 'td',
16575                                 cls : 'fc-header-right',
16576                                 cn : [
16577                               /*      fc_button('month', 'left', '', 'month' ),
16578                                     fc_button('week', '', '', 'week' ),
16579                                     fc_button('day', 'right', '', 'day' )
16580                                 */    
16581
16582                                 ]
16583                             }
16584
16585                         ]
16586                     }
16587                 ]
16588             };
16589         }
16590         
16591         header = this.header;
16592         
16593        
16594         var cal_heads = function() {
16595             var ret = [];
16596             // fixme - handle this.
16597             
16598             for (var i =0; i < Date.dayNames.length; i++) {
16599                 var d = Date.dayNames[i];
16600                 ret.push({
16601                     tag: 'th',
16602                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16603                     html : d.substring(0,3)
16604                 });
16605                 
16606             }
16607             ret[0].cls += ' fc-first';
16608             ret[6].cls += ' fc-last';
16609             return ret;
16610         };
16611         var cal_cell = function(n) {
16612             return  {
16613                 tag: 'td',
16614                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16615                 cn : [
16616                     {
16617                         cn : [
16618                             {
16619                                 cls: 'fc-day-number',
16620                                 html: 'D'
16621                             },
16622                             {
16623                                 cls: 'fc-day-content',
16624                              
16625                                 cn : [
16626                                      {
16627                                         style: 'position: relative;' // height: 17px;
16628                                     }
16629                                 ]
16630                             }
16631                             
16632                             
16633                         ]
16634                     }
16635                 ]
16636                 
16637             }
16638         };
16639         var cal_rows = function() {
16640             
16641             var ret = [];
16642             for (var r = 0; r < 6; r++) {
16643                 var row= {
16644                     tag : 'tr',
16645                     cls : 'fc-week',
16646                     cn : []
16647                 };
16648                 
16649                 for (var i =0; i < Date.dayNames.length; i++) {
16650                     var d = Date.dayNames[i];
16651                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16652
16653                 }
16654                 row.cn[0].cls+=' fc-first';
16655                 row.cn[0].cn[0].style = 'min-height:90px';
16656                 row.cn[6].cls+=' fc-last';
16657                 ret.push(row);
16658                 
16659             }
16660             ret[0].cls += ' fc-first';
16661             ret[4].cls += ' fc-prev-last';
16662             ret[5].cls += ' fc-last';
16663             return ret;
16664             
16665         };
16666         
16667         var cal_table = {
16668             tag: 'table',
16669             cls: 'fc-border-separate',
16670             style : 'width:100%',
16671             cellspacing  : 0,
16672             cn : [
16673                 { 
16674                     tag: 'thead',
16675                     cn : [
16676                         { 
16677                             tag: 'tr',
16678                             cls : 'fc-first fc-last',
16679                             cn : cal_heads()
16680                         }
16681                     ]
16682                 },
16683                 { 
16684                     tag: 'tbody',
16685                     cn : cal_rows()
16686                 }
16687                   
16688             ]
16689         };
16690          
16691          var cfg = {
16692             cls : 'fc fc-ltr',
16693             cn : [
16694                 header,
16695                 {
16696                     cls : 'fc-content',
16697                     style : "position: relative;",
16698                     cn : [
16699                         {
16700                             cls : 'fc-view fc-view-month fc-grid',
16701                             style : 'position: relative',
16702                             unselectable : 'on',
16703                             cn : [
16704                                 {
16705                                     cls : 'fc-event-container',
16706                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16707                                 },
16708                                 cal_table
16709                             ]
16710                         }
16711                     ]
16712     
16713                 }
16714            ] 
16715             
16716         };
16717         
16718          
16719         
16720         return cfg;
16721     },
16722     
16723     
16724     initEvents : function()
16725     {
16726         if(!this.store){
16727             throw "can not find store for calendar";
16728         }
16729         
16730         var mark = {
16731             tag: "div",
16732             cls:"x-dlg-mask",
16733             style: "text-align:center",
16734             cn: [
16735                 {
16736                     tag: "div",
16737                     style: "background-color:white;width:50%;margin:250 auto",
16738                     cn: [
16739                         {
16740                             tag: "img",
16741                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16742                         },
16743                         {
16744                             tag: "span",
16745                             html: "Loading"
16746                         }
16747                         
16748                     ]
16749                 }
16750             ]
16751         };
16752         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16753         
16754         var size = this.el.select('.fc-content', true).first().getSize();
16755         this.maskEl.setSize(size.width, size.height);
16756         this.maskEl.enableDisplayMode("block");
16757         if(!this.loadMask){
16758             this.maskEl.hide();
16759         }
16760         
16761         this.store = Roo.factory(this.store, Roo.data);
16762         this.store.on('load', this.onLoad, this);
16763         this.store.on('beforeload', this.onBeforeLoad, this);
16764         
16765         this.resize();
16766         
16767         this.cells = this.el.select('.fc-day',true);
16768         //Roo.log(this.cells);
16769         this.textNodes = this.el.query('.fc-day-number');
16770         this.cells.addClassOnOver('fc-state-hover');
16771         
16772         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16773         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16774         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16775         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16776         
16777         this.on('monthchange', this.onMonthChange, this);
16778         
16779         this.update(new Date().clearTime());
16780     },
16781     
16782     resize : function() {
16783         var sz  = this.el.getSize();
16784         
16785         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16786         this.el.select('.fc-day-content div',true).setHeight(34);
16787     },
16788     
16789     
16790     // private
16791     showPrevMonth : function(e){
16792         this.update(this.activeDate.add("mo", -1));
16793     },
16794     showToday : function(e){
16795         this.update(new Date().clearTime());
16796     },
16797     // private
16798     showNextMonth : function(e){
16799         this.update(this.activeDate.add("mo", 1));
16800     },
16801
16802     // private
16803     showPrevYear : function(){
16804         this.update(this.activeDate.add("y", -1));
16805     },
16806
16807     // private
16808     showNextYear : function(){
16809         this.update(this.activeDate.add("y", 1));
16810     },
16811
16812     
16813    // private
16814     update : function(date)
16815     {
16816         var vd = this.activeDate;
16817         this.activeDate = date;
16818 //        if(vd && this.el){
16819 //            var t = date.getTime();
16820 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16821 //                Roo.log('using add remove');
16822 //                
16823 //                this.fireEvent('monthchange', this, date);
16824 //                
16825 //                this.cells.removeClass("fc-state-highlight");
16826 //                this.cells.each(function(c){
16827 //                   if(c.dateValue == t){
16828 //                       c.addClass("fc-state-highlight");
16829 //                       setTimeout(function(){
16830 //                            try{c.dom.firstChild.focus();}catch(e){}
16831 //                       }, 50);
16832 //                       return false;
16833 //                   }
16834 //                   return true;
16835 //                });
16836 //                return;
16837 //            }
16838 //        }
16839         
16840         var days = date.getDaysInMonth();
16841         
16842         var firstOfMonth = date.getFirstDateOfMonth();
16843         var startingPos = firstOfMonth.getDay()-this.startDay;
16844         
16845         if(startingPos < this.startDay){
16846             startingPos += 7;
16847         }
16848         
16849         var pm = date.add(Date.MONTH, -1);
16850         var prevStart = pm.getDaysInMonth()-startingPos;
16851 //        
16852         this.cells = this.el.select('.fc-day',true);
16853         this.textNodes = this.el.query('.fc-day-number');
16854         this.cells.addClassOnOver('fc-state-hover');
16855         
16856         var cells = this.cells.elements;
16857         var textEls = this.textNodes;
16858         
16859         Roo.each(cells, function(cell){
16860             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16861         });
16862         
16863         days += startingPos;
16864
16865         // convert everything to numbers so it's fast
16866         var day = 86400000;
16867         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16868         //Roo.log(d);
16869         //Roo.log(pm);
16870         //Roo.log(prevStart);
16871         
16872         var today = new Date().clearTime().getTime();
16873         var sel = date.clearTime().getTime();
16874         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16875         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16876         var ddMatch = this.disabledDatesRE;
16877         var ddText = this.disabledDatesText;
16878         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16879         var ddaysText = this.disabledDaysText;
16880         var format = this.format;
16881         
16882         var setCellClass = function(cal, cell){
16883             cell.row = 0;
16884             cell.events = [];
16885             cell.more = [];
16886             //Roo.log('set Cell Class');
16887             cell.title = "";
16888             var t = d.getTime();
16889             
16890             //Roo.log(d);
16891             
16892             cell.dateValue = t;
16893             if(t == today){
16894                 cell.className += " fc-today";
16895                 cell.className += " fc-state-highlight";
16896                 cell.title = cal.todayText;
16897             }
16898             if(t == sel){
16899                 // disable highlight in other month..
16900                 //cell.className += " fc-state-highlight";
16901                 
16902             }
16903             // disabling
16904             if(t < min) {
16905                 cell.className = " fc-state-disabled";
16906                 cell.title = cal.minText;
16907                 return;
16908             }
16909             if(t > max) {
16910                 cell.className = " fc-state-disabled";
16911                 cell.title = cal.maxText;
16912                 return;
16913             }
16914             if(ddays){
16915                 if(ddays.indexOf(d.getDay()) != -1){
16916                     cell.title = ddaysText;
16917                     cell.className = " fc-state-disabled";
16918                 }
16919             }
16920             if(ddMatch && format){
16921                 var fvalue = d.dateFormat(format);
16922                 if(ddMatch.test(fvalue)){
16923                     cell.title = ddText.replace("%0", fvalue);
16924                     cell.className = " fc-state-disabled";
16925                 }
16926             }
16927             
16928             if (!cell.initialClassName) {
16929                 cell.initialClassName = cell.dom.className;
16930             }
16931             
16932             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16933         };
16934
16935         var i = 0;
16936         
16937         for(; i < startingPos; i++) {
16938             textEls[i].innerHTML = (++prevStart);
16939             d.setDate(d.getDate()+1);
16940             
16941             cells[i].className = "fc-past fc-other-month";
16942             setCellClass(this, cells[i]);
16943         }
16944         
16945         var intDay = 0;
16946         
16947         for(; i < days; i++){
16948             intDay = i - startingPos + 1;
16949             textEls[i].innerHTML = (intDay);
16950             d.setDate(d.getDate()+1);
16951             
16952             cells[i].className = ''; // "x-date-active";
16953             setCellClass(this, cells[i]);
16954         }
16955         var extraDays = 0;
16956         
16957         for(; i < 42; i++) {
16958             textEls[i].innerHTML = (++extraDays);
16959             d.setDate(d.getDate()+1);
16960             
16961             cells[i].className = "fc-future fc-other-month";
16962             setCellClass(this, cells[i]);
16963         }
16964         
16965         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16966         
16967         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16968         
16969         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16970         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16971         
16972         if(totalRows != 6){
16973             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16974             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16975         }
16976         
16977         this.fireEvent('monthchange', this, date);
16978         
16979         
16980         /*
16981         if(!this.internalRender){
16982             var main = this.el.dom.firstChild;
16983             var w = main.offsetWidth;
16984             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16985             Roo.fly(main).setWidth(w);
16986             this.internalRender = true;
16987             // opera does not respect the auto grow header center column
16988             // then, after it gets a width opera refuses to recalculate
16989             // without a second pass
16990             if(Roo.isOpera && !this.secondPass){
16991                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16992                 this.secondPass = true;
16993                 this.update.defer(10, this, [date]);
16994             }
16995         }
16996         */
16997         
16998     },
16999     
17000     findCell : function(dt) {
17001         dt = dt.clearTime().getTime();
17002         var ret = false;
17003         this.cells.each(function(c){
17004             //Roo.log("check " +c.dateValue + '?=' + dt);
17005             if(c.dateValue == dt){
17006                 ret = c;
17007                 return false;
17008             }
17009             return true;
17010         });
17011         
17012         return ret;
17013     },
17014     
17015     findCells : function(ev) {
17016         var s = ev.start.clone().clearTime().getTime();
17017        // Roo.log(s);
17018         var e= ev.end.clone().clearTime().getTime();
17019        // Roo.log(e);
17020         var ret = [];
17021         this.cells.each(function(c){
17022              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17023             
17024             if(c.dateValue > e){
17025                 return ;
17026             }
17027             if(c.dateValue < s){
17028                 return ;
17029             }
17030             ret.push(c);
17031         });
17032         
17033         return ret;    
17034     },
17035     
17036 //    findBestRow: function(cells)
17037 //    {
17038 //        var ret = 0;
17039 //        
17040 //        for (var i =0 ; i < cells.length;i++) {
17041 //            ret  = Math.max(cells[i].rows || 0,ret);
17042 //        }
17043 //        return ret;
17044 //        
17045 //    },
17046     
17047     
17048     addItem : function(ev)
17049     {
17050         // look for vertical location slot in
17051         var cells = this.findCells(ev);
17052         
17053 //        ev.row = this.findBestRow(cells);
17054         
17055         // work out the location.
17056         
17057         var crow = false;
17058         var rows = [];
17059         for(var i =0; i < cells.length; i++) {
17060             
17061             cells[i].row = cells[0].row;
17062             
17063             if(i == 0){
17064                 cells[i].row = cells[i].row + 1;
17065             }
17066             
17067             if (!crow) {
17068                 crow = {
17069                     start : cells[i],
17070                     end :  cells[i]
17071                 };
17072                 continue;
17073             }
17074             if (crow.start.getY() == cells[i].getY()) {
17075                 // on same row.
17076                 crow.end = cells[i];
17077                 continue;
17078             }
17079             // different row.
17080             rows.push(crow);
17081             crow = {
17082                 start: cells[i],
17083                 end : cells[i]
17084             };
17085             
17086         }
17087         
17088         rows.push(crow);
17089         ev.els = [];
17090         ev.rows = rows;
17091         ev.cells = cells;
17092         
17093         cells[0].events.push(ev);
17094         
17095         this.calevents.push(ev);
17096     },
17097     
17098     clearEvents: function() {
17099         
17100         if(!this.calevents){
17101             return;
17102         }
17103         
17104         Roo.each(this.cells.elements, function(c){
17105             c.row = 0;
17106             c.events = [];
17107             c.more = [];
17108         });
17109         
17110         Roo.each(this.calevents, function(e) {
17111             Roo.each(e.els, function(el) {
17112                 el.un('mouseenter' ,this.onEventEnter, this);
17113                 el.un('mouseleave' ,this.onEventLeave, this);
17114                 el.remove();
17115             },this);
17116         },this);
17117         
17118         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17119             e.remove();
17120         });
17121         
17122     },
17123     
17124     renderEvents: function()
17125     {   
17126         var _this = this;
17127         
17128         this.cells.each(function(c) {
17129             
17130             if(c.row < 5){
17131                 return;
17132             }
17133             
17134             var ev = c.events;
17135             
17136             var r = 4;
17137             if(c.row != c.events.length){
17138                 r = 4 - (4 - (c.row - c.events.length));
17139             }
17140             
17141             c.events = ev.slice(0, r);
17142             c.more = ev.slice(r);
17143             
17144             if(c.more.length && c.more.length == 1){
17145                 c.events.push(c.more.pop());
17146             }
17147             
17148             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17149             
17150         });
17151             
17152         this.cells.each(function(c) {
17153             
17154             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17155             
17156             
17157             for (var e = 0; e < c.events.length; e++){
17158                 var ev = c.events[e];
17159                 var rows = ev.rows;
17160                 
17161                 for(var i = 0; i < rows.length; i++) {
17162                 
17163                     // how many rows should it span..
17164
17165                     var  cfg = {
17166                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17167                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17168
17169                         unselectable : "on",
17170                         cn : [
17171                             {
17172                                 cls: 'fc-event-inner',
17173                                 cn : [
17174     //                                {
17175     //                                  tag:'span',
17176     //                                  cls: 'fc-event-time',
17177     //                                  html : cells.length > 1 ? '' : ev.time
17178     //                                },
17179                                     {
17180                                       tag:'span',
17181                                       cls: 'fc-event-title',
17182                                       html : String.format('{0}', ev.title)
17183                                     }
17184
17185
17186                                 ]
17187                             },
17188                             {
17189                                 cls: 'ui-resizable-handle ui-resizable-e',
17190                                 html : '&nbsp;&nbsp;&nbsp'
17191                             }
17192
17193                         ]
17194                     };
17195
17196                     if (i == 0) {
17197                         cfg.cls += ' fc-event-start';
17198                     }
17199                     if ((i+1) == rows.length) {
17200                         cfg.cls += ' fc-event-end';
17201                     }
17202
17203                     var ctr = _this.el.select('.fc-event-container',true).first();
17204                     var cg = ctr.createChild(cfg);
17205
17206                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17207                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17208
17209                     var r = (c.more.length) ? 1 : 0;
17210                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17211                     cg.setWidth(ebox.right - sbox.x -2);
17212
17213                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17214                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17215                     cg.on('click', _this.onEventClick, _this, ev);
17216
17217                     ev.els.push(cg);
17218                     
17219                 }
17220                 
17221             }
17222             
17223             
17224             if(c.more.length){
17225                 var  cfg = {
17226                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17227                     style : 'position: absolute',
17228                     unselectable : "on",
17229                     cn : [
17230                         {
17231                             cls: 'fc-event-inner',
17232                             cn : [
17233                                 {
17234                                   tag:'span',
17235                                   cls: 'fc-event-title',
17236                                   html : 'More'
17237                                 }
17238
17239
17240                             ]
17241                         },
17242                         {
17243                             cls: 'ui-resizable-handle ui-resizable-e',
17244                             html : '&nbsp;&nbsp;&nbsp'
17245                         }
17246
17247                     ]
17248                 };
17249
17250                 var ctr = _this.el.select('.fc-event-container',true).first();
17251                 var cg = ctr.createChild(cfg);
17252
17253                 var sbox = c.select('.fc-day-content',true).first().getBox();
17254                 var ebox = c.select('.fc-day-content',true).first().getBox();
17255                 //Roo.log(cg);
17256                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17257                 cg.setWidth(ebox.right - sbox.x -2);
17258
17259                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17260                 
17261             }
17262             
17263         });
17264         
17265         
17266         
17267     },
17268     
17269     onEventEnter: function (e, el,event,d) {
17270         this.fireEvent('evententer', this, el, event);
17271     },
17272     
17273     onEventLeave: function (e, el,event,d) {
17274         this.fireEvent('eventleave', this, el, event);
17275     },
17276     
17277     onEventClick: function (e, el,event,d) {
17278         this.fireEvent('eventclick', this, el, event);
17279     },
17280     
17281     onMonthChange: function () {
17282         this.store.load();
17283     },
17284     
17285     onMoreEventClick: function(e, el, more)
17286     {
17287         var _this = this;
17288         
17289         this.calpopover.placement = 'right';
17290         this.calpopover.setTitle('More');
17291         
17292         this.calpopover.setContent('');
17293         
17294         var ctr = this.calpopover.el.select('.popover-content', true).first();
17295         
17296         Roo.each(more, function(m){
17297             var cfg = {
17298                 cls : 'fc-event-hori fc-event-draggable',
17299                 html : m.title
17300             };
17301             var cg = ctr.createChild(cfg);
17302             
17303             cg.on('click', _this.onEventClick, _this, m);
17304         });
17305         
17306         this.calpopover.show(el);
17307         
17308         
17309     },
17310     
17311     onLoad: function () 
17312     {   
17313         this.calevents = [];
17314         var cal = this;
17315         
17316         if(this.store.getCount() > 0){
17317             this.store.data.each(function(d){
17318                cal.addItem({
17319                     id : d.data.id,
17320                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17321                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17322                     time : d.data.start_time,
17323                     title : d.data.title,
17324                     description : d.data.description,
17325                     venue : d.data.venue
17326                 });
17327             });
17328         }
17329         
17330         this.renderEvents();
17331         
17332         if(this.calevents.length && this.loadMask){
17333             this.maskEl.hide();
17334         }
17335     },
17336     
17337     onBeforeLoad: function()
17338     {
17339         this.clearEvents();
17340         if(this.loadMask){
17341             this.maskEl.show();
17342         }
17343     }
17344 });
17345
17346  
17347  /*
17348  * - LGPL
17349  *
17350  * element
17351  * 
17352  */
17353
17354 /**
17355  * @class Roo.bootstrap.Popover
17356  * @extends Roo.bootstrap.Component
17357  * Bootstrap Popover class
17358  * @cfg {String} html contents of the popover   (or false to use children..)
17359  * @cfg {String} title of popover (or false to hide)
17360  * @cfg {String} placement how it is placed
17361  * @cfg {String} trigger click || hover (or false to trigger manually)
17362  * @cfg {String} over what (parent or false to trigger manually.)
17363  * @cfg {Number} delay - delay before showing
17364  
17365  * @constructor
17366  * Create a new Popover
17367  * @param {Object} config The config object
17368  */
17369
17370 Roo.bootstrap.Popover = function(config){
17371     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17372     
17373     this.addEvents({
17374         // raw events
17375          /**
17376          * @event show
17377          * After the popover show
17378          * 
17379          * @param {Roo.bootstrap.Popover} this
17380          */
17381         "show" : true,
17382         /**
17383          * @event hide
17384          * After the popover hide
17385          * 
17386          * @param {Roo.bootstrap.Popover} this
17387          */
17388         "hide" : true
17389     });
17390 };
17391
17392 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17393     
17394     title: 'Fill in a title',
17395     html: false,
17396     
17397     placement : 'right',
17398     trigger : 'hover', // hover
17399     
17400     delay : 0,
17401     
17402     over: 'parent',
17403     
17404     can_build_overlaid : false,
17405     
17406     getChildContainer : function()
17407     {
17408         return this.el.select('.popover-content',true).first();
17409     },
17410     
17411     getAutoCreate : function(){
17412          
17413         var cfg = {
17414            cls : 'popover roo-dynamic',
17415            style: 'display:block',
17416            cn : [
17417                 {
17418                     cls : 'arrow'
17419                 },
17420                 {
17421                     cls : 'popover-inner',
17422                     cn : [
17423                         {
17424                             tag: 'h3',
17425                             cls: 'popover-title',
17426                             html : this.title
17427                         },
17428                         {
17429                             cls : 'popover-content',
17430                             html : this.html
17431                         }
17432                     ]
17433                     
17434                 }
17435            ]
17436         };
17437         
17438         return cfg;
17439     },
17440     setTitle: function(str)
17441     {
17442         this.title = str;
17443         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17444     },
17445     setContent: function(str)
17446     {
17447         this.html = str;
17448         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17449     },
17450     // as it get's added to the bottom of the page.
17451     onRender : function(ct, position)
17452     {
17453         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17454         if(!this.el){
17455             var cfg = Roo.apply({},  this.getAutoCreate());
17456             cfg.id = Roo.id();
17457             
17458             if (this.cls) {
17459                 cfg.cls += ' ' + this.cls;
17460             }
17461             if (this.style) {
17462                 cfg.style = this.style;
17463             }
17464             //Roo.log("adding to ");
17465             this.el = Roo.get(document.body).createChild(cfg, position);
17466 //            Roo.log(this.el);
17467         }
17468         this.initEvents();
17469     },
17470     
17471     initEvents : function()
17472     {
17473         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17474         this.el.enableDisplayMode('block');
17475         this.el.hide();
17476         if (this.over === false) {
17477             return; 
17478         }
17479         if (this.triggers === false) {
17480             return;
17481         }
17482         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17483         var triggers = this.trigger ? this.trigger.split(' ') : [];
17484         Roo.each(triggers, function(trigger) {
17485         
17486             if (trigger == 'click') {
17487                 on_el.on('click', this.toggle, this);
17488             } else if (trigger != 'manual') {
17489                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17490                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17491       
17492                 on_el.on(eventIn  ,this.enter, this);
17493                 on_el.on(eventOut, this.leave, this);
17494             }
17495         }, this);
17496         
17497     },
17498     
17499     
17500     // private
17501     timeout : null,
17502     hoverState : null,
17503     
17504     toggle : function () {
17505         this.hoverState == 'in' ? this.leave() : this.enter();
17506     },
17507     
17508     enter : function () {
17509         
17510         clearTimeout(this.timeout);
17511     
17512         this.hoverState = 'in';
17513     
17514         if (!this.delay || !this.delay.show) {
17515             this.show();
17516             return;
17517         }
17518         var _t = this;
17519         this.timeout = setTimeout(function () {
17520             if (_t.hoverState == 'in') {
17521                 _t.show();
17522             }
17523         }, this.delay.show)
17524     },
17525     
17526     leave : function() {
17527         clearTimeout(this.timeout);
17528     
17529         this.hoverState = 'out';
17530     
17531         if (!this.delay || !this.delay.hide) {
17532             this.hide();
17533             return;
17534         }
17535         var _t = this;
17536         this.timeout = setTimeout(function () {
17537             if (_t.hoverState == 'out') {
17538                 _t.hide();
17539             }
17540         }, this.delay.hide)
17541     },
17542     
17543     show : function (on_el)
17544     {
17545         if (!on_el) {
17546             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17547         }
17548         
17549         // set content.
17550         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17551         if (this.html !== false) {
17552             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17553         }
17554         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17555         if (!this.title.length) {
17556             this.el.select('.popover-title',true).hide();
17557         }
17558         
17559         var placement = typeof this.placement == 'function' ?
17560             this.placement.call(this, this.el, on_el) :
17561             this.placement;
17562             
17563         var autoToken = /\s?auto?\s?/i;
17564         var autoPlace = autoToken.test(placement);
17565         if (autoPlace) {
17566             placement = placement.replace(autoToken, '') || 'top';
17567         }
17568         
17569         //this.el.detach()
17570         //this.el.setXY([0,0]);
17571         this.el.show();
17572         this.el.dom.style.display='block';
17573         this.el.addClass(placement);
17574         
17575         //this.el.appendTo(on_el);
17576         
17577         var p = this.getPosition();
17578         var box = this.el.getBox();
17579         
17580         if (autoPlace) {
17581             // fixme..
17582         }
17583         var align = Roo.bootstrap.Popover.alignment[placement];
17584         
17585 //        Roo.log(align);
17586         this.el.alignTo(on_el, align[0],align[1]);
17587         //var arrow = this.el.select('.arrow',true).first();
17588         //arrow.set(align[2], 
17589         
17590         this.el.addClass('in');
17591         
17592         
17593         if (this.el.hasClass('fade')) {
17594             // fade it?
17595         }
17596         
17597         this.hoverState = 'in';
17598         
17599         this.fireEvent('show', this);
17600         
17601     },
17602     hide : function()
17603     {
17604         this.el.setXY([0,0]);
17605         this.el.removeClass('in');
17606         this.el.hide();
17607         this.hoverState = null;
17608         
17609         this.fireEvent('hide', this);
17610     }
17611     
17612 });
17613
17614 Roo.bootstrap.Popover.alignment = {
17615     'left' : ['r-l', [-10,0], 'right'],
17616     'right' : ['l-r', [10,0], 'left'],
17617     'bottom' : ['t-b', [0,10], 'top'],
17618     'top' : [ 'b-t', [0,-10], 'bottom']
17619 };
17620
17621  /*
17622  * - LGPL
17623  *
17624  * Progress
17625  * 
17626  */
17627
17628 /**
17629  * @class Roo.bootstrap.Progress
17630  * @extends Roo.bootstrap.Component
17631  * Bootstrap Progress class
17632  * @cfg {Boolean} striped striped of the progress bar
17633  * @cfg {Boolean} active animated of the progress bar
17634  * 
17635  * 
17636  * @constructor
17637  * Create a new Progress
17638  * @param {Object} config The config object
17639  */
17640
17641 Roo.bootstrap.Progress = function(config){
17642     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17643 };
17644
17645 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17646     
17647     striped : false,
17648     active: false,
17649     
17650     getAutoCreate : function(){
17651         var cfg = {
17652             tag: 'div',
17653             cls: 'progress'
17654         };
17655         
17656         
17657         if(this.striped){
17658             cfg.cls += ' progress-striped';
17659         }
17660       
17661         if(this.active){
17662             cfg.cls += ' active';
17663         }
17664         
17665         
17666         return cfg;
17667     }
17668    
17669 });
17670
17671  
17672
17673  /*
17674  * - LGPL
17675  *
17676  * ProgressBar
17677  * 
17678  */
17679
17680 /**
17681  * @class Roo.bootstrap.ProgressBar
17682  * @extends Roo.bootstrap.Component
17683  * Bootstrap ProgressBar class
17684  * @cfg {Number} aria_valuenow aria-value now
17685  * @cfg {Number} aria_valuemin aria-value min
17686  * @cfg {Number} aria_valuemax aria-value max
17687  * @cfg {String} label label for the progress bar
17688  * @cfg {String} panel (success | info | warning | danger )
17689  * @cfg {String} role role of the progress bar
17690  * @cfg {String} sr_only text
17691  * 
17692  * 
17693  * @constructor
17694  * Create a new ProgressBar
17695  * @param {Object} config The config object
17696  */
17697
17698 Roo.bootstrap.ProgressBar = function(config){
17699     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17700 };
17701
17702 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17703     
17704     aria_valuenow : 0,
17705     aria_valuemin : 0,
17706     aria_valuemax : 100,
17707     label : false,
17708     panel : false,
17709     role : false,
17710     sr_only: false,
17711     
17712     getAutoCreate : function()
17713     {
17714         
17715         var cfg = {
17716             tag: 'div',
17717             cls: 'progress-bar',
17718             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17719         };
17720         
17721         if(this.sr_only){
17722             cfg.cn = {
17723                 tag: 'span',
17724                 cls: 'sr-only',
17725                 html: this.sr_only
17726             }
17727         }
17728         
17729         if(this.role){
17730             cfg.role = this.role;
17731         }
17732         
17733         if(this.aria_valuenow){
17734             cfg['aria-valuenow'] = this.aria_valuenow;
17735         }
17736         
17737         if(this.aria_valuemin){
17738             cfg['aria-valuemin'] = this.aria_valuemin;
17739         }
17740         
17741         if(this.aria_valuemax){
17742             cfg['aria-valuemax'] = this.aria_valuemax;
17743         }
17744         
17745         if(this.label && !this.sr_only){
17746             cfg.html = this.label;
17747         }
17748         
17749         if(this.panel){
17750             cfg.cls += ' progress-bar-' + this.panel;
17751         }
17752         
17753         return cfg;
17754     },
17755     
17756     update : function(aria_valuenow)
17757     {
17758         this.aria_valuenow = aria_valuenow;
17759         
17760         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17761     }
17762    
17763 });
17764
17765  
17766
17767  /*
17768  * - LGPL
17769  *
17770  * column
17771  * 
17772  */
17773
17774 /**
17775  * @class Roo.bootstrap.TabGroup
17776  * @extends Roo.bootstrap.Column
17777  * Bootstrap Column class
17778  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17779  * @cfg {Boolean} carousel true to make the group behave like a carousel
17780  * @cfg {Boolean} bullets show bullets for the panels
17781  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17782  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17783  * @cfg {Boolean} showarrow (true|false) show arrow default true
17784  * 
17785  * @constructor
17786  * Create a new TabGroup
17787  * @param {Object} config The config object
17788  */
17789
17790 Roo.bootstrap.TabGroup = function(config){
17791     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17792     if (!this.navId) {
17793         this.navId = Roo.id();
17794     }
17795     this.tabs = [];
17796     Roo.bootstrap.TabGroup.register(this);
17797     
17798 };
17799
17800 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17801     
17802     carousel : false,
17803     transition : false,
17804     bullets : 0,
17805     timer : 0,
17806     autoslide : false,
17807     slideFn : false,
17808     slideOnTouch : false,
17809     showarrow : true,
17810     
17811     getAutoCreate : function()
17812     {
17813         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17814         
17815         cfg.cls += ' tab-content';
17816         
17817         if (this.carousel) {
17818             cfg.cls += ' carousel slide';
17819             
17820             cfg.cn = [{
17821                cls : 'carousel-inner',
17822                cn : []
17823             }];
17824         
17825             if(this.bullets  && !Roo.isTouch){
17826                 
17827                 var bullets = {
17828                     cls : 'carousel-bullets',
17829                     cn : []
17830                 };
17831                
17832                 if(this.bullets_cls){
17833                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17834                 }
17835                 
17836                 bullets.cn.push({
17837                     cls : 'clear'
17838                 });
17839                 
17840                 cfg.cn[0].cn.push(bullets);
17841             }
17842             
17843             if(this.showarrow){
17844                 cfg.cn[0].cn.push({
17845                     tag : 'div',
17846                     class : 'carousel-arrow',
17847                     cn : [
17848                         {
17849                             tag : 'div',
17850                             class : 'carousel-prev',
17851                             cn : [
17852                                 {
17853                                     tag : 'i',
17854                                     class : 'fa fa-chevron-left'
17855                                 }
17856                             ]
17857                         },
17858                         {
17859                             tag : 'div',
17860                             class : 'carousel-next',
17861                             cn : [
17862                                 {
17863                                     tag : 'i',
17864                                     class : 'fa fa-chevron-right'
17865                                 }
17866                             ]
17867                         }
17868                     ]
17869                 });
17870             }
17871             
17872         }
17873         
17874         return cfg;
17875     },
17876     
17877     initEvents:  function()
17878     {
17879 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17880 //            this.el.on("touchstart", this.onTouchStart, this);
17881 //        }
17882         
17883         if(this.autoslide){
17884             var _this = this;
17885             
17886             this.slideFn = window.setInterval(function() {
17887                 _this.showPanelNext();
17888             }, this.timer);
17889         }
17890         
17891         if(this.showarrow){
17892             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17893             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17894         }
17895         
17896         
17897     },
17898     
17899 //    onTouchStart : function(e, el, o)
17900 //    {
17901 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17902 //            return;
17903 //        }
17904 //        
17905 //        this.showPanelNext();
17906 //    },
17907     
17908     
17909     getChildContainer : function()
17910     {
17911         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17912     },
17913     
17914     /**
17915     * register a Navigation item
17916     * @param {Roo.bootstrap.NavItem} the navitem to add
17917     */
17918     register : function(item)
17919     {
17920         this.tabs.push( item);
17921         item.navId = this.navId; // not really needed..
17922         this.addBullet();
17923     
17924     },
17925     
17926     getActivePanel : function()
17927     {
17928         var r = false;
17929         Roo.each(this.tabs, function(t) {
17930             if (t.active) {
17931                 r = t;
17932                 return false;
17933             }
17934             return null;
17935         });
17936         return r;
17937         
17938     },
17939     getPanelByName : function(n)
17940     {
17941         var r = false;
17942         Roo.each(this.tabs, function(t) {
17943             if (t.tabId == n) {
17944                 r = t;
17945                 return false;
17946             }
17947             return null;
17948         });
17949         return r;
17950     },
17951     indexOfPanel : function(p)
17952     {
17953         var r = false;
17954         Roo.each(this.tabs, function(t,i) {
17955             if (t.tabId == p.tabId) {
17956                 r = i;
17957                 return false;
17958             }
17959             return null;
17960         });
17961         return r;
17962     },
17963     /**
17964      * show a specific panel
17965      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17966      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17967      */
17968     showPanel : function (pan)
17969     {
17970         if(this.transition || typeof(pan) == 'undefined'){
17971             Roo.log("waiting for the transitionend");
17972             return;
17973         }
17974         
17975         if (typeof(pan) == 'number') {
17976             pan = this.tabs[pan];
17977         }
17978         
17979         if (typeof(pan) == 'string') {
17980             pan = this.getPanelByName(pan);
17981         }
17982         
17983         var cur = this.getActivePanel();
17984         
17985         if(!pan || !cur){
17986             Roo.log('pan or acitve pan is undefined');
17987             return false;
17988         }
17989         
17990         if (pan.tabId == this.getActivePanel().tabId) {
17991             return true;
17992         }
17993         
17994         if (false === cur.fireEvent('beforedeactivate')) {
17995             return false;
17996         }
17997         
17998         if(this.bullets > 0 && !Roo.isTouch){
17999             this.setActiveBullet(this.indexOfPanel(pan));
18000         }
18001         
18002         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18003             
18004             this.transition = true;
18005             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18006             var lr = dir == 'next' ? 'left' : 'right';
18007             pan.el.addClass(dir); // or prev
18008             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18009             cur.el.addClass(lr); // or right
18010             pan.el.addClass(lr);
18011             
18012             var _this = this;
18013             cur.el.on('transitionend', function() {
18014                 Roo.log("trans end?");
18015                 
18016                 pan.el.removeClass([lr,dir]);
18017                 pan.setActive(true);
18018                 
18019                 cur.el.removeClass([lr]);
18020                 cur.setActive(false);
18021                 
18022                 _this.transition = false;
18023                 
18024             }, this, { single:  true } );
18025             
18026             return true;
18027         }
18028         
18029         cur.setActive(false);
18030         pan.setActive(true);
18031         
18032         return true;
18033         
18034     },
18035     showPanelNext : function()
18036     {
18037         var i = this.indexOfPanel(this.getActivePanel());
18038         
18039         if (i >= this.tabs.length - 1 && !this.autoslide) {
18040             return;
18041         }
18042         
18043         if (i >= this.tabs.length - 1 && this.autoslide) {
18044             i = -1;
18045         }
18046         
18047         this.showPanel(this.tabs[i+1]);
18048     },
18049     
18050     showPanelPrev : function()
18051     {
18052         var i = this.indexOfPanel(this.getActivePanel());
18053         
18054         if (i  < 1 && !this.autoslide) {
18055             return;
18056         }
18057         
18058         if (i < 1 && this.autoslide) {
18059             i = this.tabs.length;
18060         }
18061         
18062         this.showPanel(this.tabs[i-1]);
18063     },
18064     
18065     
18066     addBullet: function()
18067     {
18068         if(!this.bullets || Roo.isTouch){
18069             return;
18070         }
18071         var ctr = this.el.select('.carousel-bullets',true).first();
18072         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18073         var bullet = ctr.createChild({
18074             cls : 'bullet bullet-' + i
18075         },ctr.dom.lastChild);
18076         
18077         
18078         var _this = this;
18079         
18080         bullet.on('click', (function(e, el, o, ii, t){
18081
18082             e.preventDefault();
18083
18084             this.showPanel(ii);
18085
18086             if(this.autoslide && this.slideFn){
18087                 clearInterval(this.slideFn);
18088                 this.slideFn = window.setInterval(function() {
18089                     _this.showPanelNext();
18090                 }, this.timer);
18091             }
18092
18093         }).createDelegate(this, [i, bullet], true));
18094                 
18095         
18096     },
18097      
18098     setActiveBullet : function(i)
18099     {
18100         if(Roo.isTouch){
18101             return;
18102         }
18103         
18104         Roo.each(this.el.select('.bullet', true).elements, function(el){
18105             el.removeClass('selected');
18106         });
18107
18108         var bullet = this.el.select('.bullet-' + i, true).first();
18109         
18110         if(!bullet){
18111             return;
18112         }
18113         
18114         bullet.addClass('selected');
18115     }
18116     
18117     
18118   
18119 });
18120
18121  
18122
18123  
18124  
18125 Roo.apply(Roo.bootstrap.TabGroup, {
18126     
18127     groups: {},
18128      /**
18129     * register a Navigation Group
18130     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18131     */
18132     register : function(navgrp)
18133     {
18134         this.groups[navgrp.navId] = navgrp;
18135         
18136     },
18137     /**
18138     * fetch a Navigation Group based on the navigation ID
18139     * if one does not exist , it will get created.
18140     * @param {string} the navgroup to add
18141     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18142     */
18143     get: function(navId) {
18144         if (typeof(this.groups[navId]) == 'undefined') {
18145             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18146         }
18147         return this.groups[navId] ;
18148     }
18149     
18150     
18151     
18152 });
18153
18154  /*
18155  * - LGPL
18156  *
18157  * TabPanel
18158  * 
18159  */
18160
18161 /**
18162  * @class Roo.bootstrap.TabPanel
18163  * @extends Roo.bootstrap.Component
18164  * Bootstrap TabPanel class
18165  * @cfg {Boolean} active panel active
18166  * @cfg {String} html panel content
18167  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18168  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18169  * @cfg {String} href click to link..
18170  * 
18171  * 
18172  * @constructor
18173  * Create a new TabPanel
18174  * @param {Object} config The config object
18175  */
18176
18177 Roo.bootstrap.TabPanel = function(config){
18178     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18179     this.addEvents({
18180         /**
18181              * @event changed
18182              * Fires when the active status changes
18183              * @param {Roo.bootstrap.TabPanel} this
18184              * @param {Boolean} state the new state
18185             
18186          */
18187         'changed': true,
18188         /**
18189              * @event beforedeactivate
18190              * Fires before a tab is de-activated - can be used to do validation on a form.
18191              * @param {Roo.bootstrap.TabPanel} this
18192              * @return {Boolean} false if there is an error
18193             
18194          */
18195         'beforedeactivate': true
18196      });
18197     
18198     this.tabId = this.tabId || Roo.id();
18199   
18200 };
18201
18202 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18203     
18204     active: false,
18205     html: false,
18206     tabId: false,
18207     navId : false,
18208     href : '',
18209     
18210     getAutoCreate : function(){
18211         var cfg = {
18212             tag: 'div',
18213             // item is needed for carousel - not sure if it has any effect otherwise
18214             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18215             html: this.html || ''
18216         };
18217         
18218         if(this.active){
18219             cfg.cls += ' active';
18220         }
18221         
18222         if(this.tabId){
18223             cfg.tabId = this.tabId;
18224         }
18225         
18226         
18227         return cfg;
18228     },
18229     
18230     initEvents:  function()
18231     {
18232         var p = this.parent();
18233         
18234         this.navId = this.navId || p.navId;
18235         
18236         if (typeof(this.navId) != 'undefined') {
18237             // not really needed.. but just in case.. parent should be a NavGroup.
18238             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18239             
18240             tg.register(this);
18241             
18242             var i = tg.tabs.length - 1;
18243             
18244             if(this.active && tg.bullets > 0 && i < tg.bullets){
18245                 tg.setActiveBullet(i);
18246             }
18247         }
18248         
18249         this.el.on('click', this.onClick, this);
18250         
18251         if(Roo.isTouch){
18252             this.el.on("touchstart", this.onTouchStart, this);
18253             this.el.on("touchmove", this.onTouchMove, this);
18254             this.el.on("touchend", this.onTouchEnd, this);
18255         }
18256         
18257     },
18258     
18259     onRender : function(ct, position)
18260     {
18261         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18262     },
18263     
18264     setActive : function(state)
18265     {
18266         Roo.log("panel - set active " + this.tabId + "=" + state);
18267         
18268         this.active = state;
18269         if (!state) {
18270             this.el.removeClass('active');
18271             
18272         } else  if (!this.el.hasClass('active')) {
18273             this.el.addClass('active');
18274         }
18275         
18276         this.fireEvent('changed', this, state);
18277     },
18278     
18279     onClick : function(e)
18280     {
18281         e.preventDefault();
18282         
18283         if(!this.href.length){
18284             return;
18285         }
18286         
18287         window.location.href = this.href;
18288     },
18289     
18290     startX : 0,
18291     startY : 0,
18292     endX : 0,
18293     endY : 0,
18294     swiping : false,
18295     
18296     onTouchStart : function(e)
18297     {
18298         this.swiping = false;
18299         
18300         this.startX = e.browserEvent.touches[0].clientX;
18301         this.startY = e.browserEvent.touches[0].clientY;
18302     },
18303     
18304     onTouchMove : function(e)
18305     {
18306         this.swiping = true;
18307         
18308         this.endX = e.browserEvent.touches[0].clientX;
18309         this.endY = e.browserEvent.touches[0].clientY;
18310     },
18311     
18312     onTouchEnd : function(e)
18313     {
18314         if(!this.swiping){
18315             this.onClick(e);
18316             return;
18317         }
18318         
18319         var tabGroup = this.parent();
18320         
18321         if(this.endX > this.startX){ // swiping right
18322             tabGroup.showPanelPrev();
18323             return;
18324         }
18325         
18326         if(this.startX > this.endX){ // swiping left
18327             tabGroup.showPanelNext();
18328             return;
18329         }
18330     }
18331     
18332     
18333 });
18334  
18335
18336  
18337
18338  /*
18339  * - LGPL
18340  *
18341  * DateField
18342  * 
18343  */
18344
18345 /**
18346  * @class Roo.bootstrap.DateField
18347  * @extends Roo.bootstrap.Input
18348  * Bootstrap DateField class
18349  * @cfg {Number} weekStart default 0
18350  * @cfg {String} viewMode default empty, (months|years)
18351  * @cfg {String} minViewMode default empty, (months|years)
18352  * @cfg {Number} startDate default -Infinity
18353  * @cfg {Number} endDate default Infinity
18354  * @cfg {Boolean} todayHighlight default false
18355  * @cfg {Boolean} todayBtn default false
18356  * @cfg {Boolean} calendarWeeks default false
18357  * @cfg {Object} daysOfWeekDisabled default empty
18358  * @cfg {Boolean} singleMode default false (true | false)
18359  * 
18360  * @cfg {Boolean} keyboardNavigation default true
18361  * @cfg {String} language default en
18362  * 
18363  * @constructor
18364  * Create a new DateField
18365  * @param {Object} config The config object
18366  */
18367
18368 Roo.bootstrap.DateField = function(config){
18369     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18370      this.addEvents({
18371             /**
18372              * @event show
18373              * Fires when this field show.
18374              * @param {Roo.bootstrap.DateField} this
18375              * @param {Mixed} date The date value
18376              */
18377             show : true,
18378             /**
18379              * @event show
18380              * Fires when this field hide.
18381              * @param {Roo.bootstrap.DateField} this
18382              * @param {Mixed} date The date value
18383              */
18384             hide : true,
18385             /**
18386              * @event select
18387              * Fires when select a date.
18388              * @param {Roo.bootstrap.DateField} this
18389              * @param {Mixed} date The date value
18390              */
18391             select : true,
18392             /**
18393              * @event beforeselect
18394              * Fires when before select a date.
18395              * @param {Roo.bootstrap.DateField} this
18396              * @param {Mixed} date The date value
18397              */
18398             beforeselect : true
18399         });
18400 };
18401
18402 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18403     
18404     /**
18405      * @cfg {String} format
18406      * The default date format string which can be overriden for localization support.  The format must be
18407      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18408      */
18409     format : "m/d/y",
18410     /**
18411      * @cfg {String} altFormats
18412      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18413      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18414      */
18415     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18416     
18417     weekStart : 0,
18418     
18419     viewMode : '',
18420     
18421     minViewMode : '',
18422     
18423     todayHighlight : false,
18424     
18425     todayBtn: false,
18426     
18427     language: 'en',
18428     
18429     keyboardNavigation: true,
18430     
18431     calendarWeeks: false,
18432     
18433     startDate: -Infinity,
18434     
18435     endDate: Infinity,
18436     
18437     daysOfWeekDisabled: [],
18438     
18439     _events: [],
18440     
18441     singleMode : false,
18442     
18443     UTCDate: function()
18444     {
18445         return new Date(Date.UTC.apply(Date, arguments));
18446     },
18447     
18448     UTCToday: function()
18449     {
18450         var today = new Date();
18451         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18452     },
18453     
18454     getDate: function() {
18455             var d = this.getUTCDate();
18456             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18457     },
18458     
18459     getUTCDate: function() {
18460             return this.date;
18461     },
18462     
18463     setDate: function(d) {
18464             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18465     },
18466     
18467     setUTCDate: function(d) {
18468             this.date = d;
18469             this.setValue(this.formatDate(this.date));
18470     },
18471         
18472     onRender: function(ct, position)
18473     {
18474         
18475         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18476         
18477         this.language = this.language || 'en';
18478         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18479         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18480         
18481         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18482         this.format = this.format || 'm/d/y';
18483         this.isInline = false;
18484         this.isInput = true;
18485         this.component = this.el.select('.add-on', true).first() || false;
18486         this.component = (this.component && this.component.length === 0) ? false : this.component;
18487         this.hasInput = this.component && this.inputEl().length;
18488         
18489         if (typeof(this.minViewMode === 'string')) {
18490             switch (this.minViewMode) {
18491                 case 'months':
18492                     this.minViewMode = 1;
18493                     break;
18494                 case 'years':
18495                     this.minViewMode = 2;
18496                     break;
18497                 default:
18498                     this.minViewMode = 0;
18499                     break;
18500             }
18501         }
18502         
18503         if (typeof(this.viewMode === 'string')) {
18504             switch (this.viewMode) {
18505                 case 'months':
18506                     this.viewMode = 1;
18507                     break;
18508                 case 'years':
18509                     this.viewMode = 2;
18510                     break;
18511                 default:
18512                     this.viewMode = 0;
18513                     break;
18514             }
18515         }
18516                 
18517         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18518         
18519 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18520         
18521         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18522         
18523         this.picker().on('mousedown', this.onMousedown, this);
18524         this.picker().on('click', this.onClick, this);
18525         
18526         this.picker().addClass('datepicker-dropdown');
18527         
18528         this.startViewMode = this.viewMode;
18529         
18530         if(this.singleMode){
18531             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18532                 v.setVisibilityMode(Roo.Element.DISPLAY);
18533                 v.hide();
18534             });
18535             
18536             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18537                 v.setStyle('width', '189px');
18538             });
18539         }
18540         
18541         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18542             if(!this.calendarWeeks){
18543                 v.remove();
18544                 return;
18545             }
18546             
18547             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18548             v.attr('colspan', function(i, val){
18549                 return parseInt(val) + 1;
18550             });
18551         });
18552                         
18553         
18554         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18555         
18556         this.setStartDate(this.startDate);
18557         this.setEndDate(this.endDate);
18558         
18559         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18560         
18561         this.fillDow();
18562         this.fillMonths();
18563         this.update();
18564         this.showMode();
18565         
18566         if(this.isInline) {
18567             this.showPopup();
18568         }
18569     },
18570     
18571     picker : function()
18572     {
18573         return this.pickerEl;
18574 //        return this.el.select('.datepicker', true).first();
18575     },
18576     
18577     fillDow: function()
18578     {
18579         var dowCnt = this.weekStart;
18580         
18581         var dow = {
18582             tag: 'tr',
18583             cn: [
18584                 
18585             ]
18586         };
18587         
18588         if(this.calendarWeeks){
18589             dow.cn.push({
18590                 tag: 'th',
18591                 cls: 'cw',
18592                 html: '&nbsp;'
18593             })
18594         }
18595         
18596         while (dowCnt < this.weekStart + 7) {
18597             dow.cn.push({
18598                 tag: 'th',
18599                 cls: 'dow',
18600                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18601             });
18602         }
18603         
18604         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18605     },
18606     
18607     fillMonths: function()
18608     {    
18609         var i = 0;
18610         var months = this.picker().select('>.datepicker-months td', true).first();
18611         
18612         months.dom.innerHTML = '';
18613         
18614         while (i < 12) {
18615             var month = {
18616                 tag: 'span',
18617                 cls: 'month',
18618                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18619             };
18620             
18621             months.createChild(month);
18622         }
18623         
18624     },
18625     
18626     update: function()
18627     {
18628         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;
18629         
18630         if (this.date < this.startDate) {
18631             this.viewDate = new Date(this.startDate);
18632         } else if (this.date > this.endDate) {
18633             this.viewDate = new Date(this.endDate);
18634         } else {
18635             this.viewDate = new Date(this.date);
18636         }
18637         
18638         this.fill();
18639     },
18640     
18641     fill: function() 
18642     {
18643         var d = new Date(this.viewDate),
18644                 year = d.getUTCFullYear(),
18645                 month = d.getUTCMonth(),
18646                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18647                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18648                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18649                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18650                 currentDate = this.date && this.date.valueOf(),
18651                 today = this.UTCToday();
18652         
18653         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18654         
18655 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18656         
18657 //        this.picker.select('>tfoot th.today').
18658 //                                              .text(dates[this.language].today)
18659 //                                              .toggle(this.todayBtn !== false);
18660     
18661         this.updateNavArrows();
18662         this.fillMonths();
18663                                                 
18664         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18665         
18666         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18667          
18668         prevMonth.setUTCDate(day);
18669         
18670         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18671         
18672         var nextMonth = new Date(prevMonth);
18673         
18674         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18675         
18676         nextMonth = nextMonth.valueOf();
18677         
18678         var fillMonths = false;
18679         
18680         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18681         
18682         while(prevMonth.valueOf() <= nextMonth) {
18683             var clsName = '';
18684             
18685             if (prevMonth.getUTCDay() === this.weekStart) {
18686                 if(fillMonths){
18687                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18688                 }
18689                     
18690                 fillMonths = {
18691                     tag: 'tr',
18692                     cn: []
18693                 };
18694                 
18695                 if(this.calendarWeeks){
18696                     // ISO 8601: First week contains first thursday.
18697                     // ISO also states week starts on Monday, but we can be more abstract here.
18698                     var
18699                     // Start of current week: based on weekstart/current date
18700                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18701                     // Thursday of this week
18702                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18703                     // First Thursday of year, year from thursday
18704                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18705                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18706                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18707                     
18708                     fillMonths.cn.push({
18709                         tag: 'td',
18710                         cls: 'cw',
18711                         html: calWeek
18712                     });
18713                 }
18714             }
18715             
18716             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18717                 clsName += ' old';
18718             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18719                 clsName += ' new';
18720             }
18721             if (this.todayHighlight &&
18722                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18723                 prevMonth.getUTCMonth() == today.getMonth() &&
18724                 prevMonth.getUTCDate() == today.getDate()) {
18725                 clsName += ' today';
18726             }
18727             
18728             if (currentDate && prevMonth.valueOf() === currentDate) {
18729                 clsName += ' active';
18730             }
18731             
18732             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18733                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18734                     clsName += ' disabled';
18735             }
18736             
18737             fillMonths.cn.push({
18738                 tag: 'td',
18739                 cls: 'day ' + clsName,
18740                 html: prevMonth.getDate()
18741             });
18742             
18743             prevMonth.setDate(prevMonth.getDate()+1);
18744         }
18745           
18746         var currentYear = this.date && this.date.getUTCFullYear();
18747         var currentMonth = this.date && this.date.getUTCMonth();
18748         
18749         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18750         
18751         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18752             v.removeClass('active');
18753             
18754             if(currentYear === year && k === currentMonth){
18755                 v.addClass('active');
18756             }
18757             
18758             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18759                 v.addClass('disabled');
18760             }
18761             
18762         });
18763         
18764         
18765         year = parseInt(year/10, 10) * 10;
18766         
18767         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18768         
18769         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18770         
18771         year -= 1;
18772         for (var i = -1; i < 11; i++) {
18773             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18774                 tag: 'span',
18775                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18776                 html: year
18777             });
18778             
18779             year += 1;
18780         }
18781     },
18782     
18783     showMode: function(dir) 
18784     {
18785         if (dir) {
18786             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18787         }
18788         
18789         Roo.each(this.picker().select('>div',true).elements, function(v){
18790             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18791             v.hide();
18792         });
18793         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18794     },
18795     
18796     place: function()
18797     {
18798         if(this.isInline) {
18799             return;
18800         }
18801         
18802         this.picker().removeClass(['bottom', 'top']);
18803         
18804         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18805             /*
18806              * place to the top of element!
18807              *
18808              */
18809             
18810             this.picker().addClass('top');
18811             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18812             
18813             return;
18814         }
18815         
18816         this.picker().addClass('bottom');
18817         
18818         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18819     },
18820     
18821     parseDate : function(value)
18822     {
18823         if(!value || value instanceof Date){
18824             return value;
18825         }
18826         var v = Date.parseDate(value, this.format);
18827         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18828             v = Date.parseDate(value, 'Y-m-d');
18829         }
18830         if(!v && this.altFormats){
18831             if(!this.altFormatsArray){
18832                 this.altFormatsArray = this.altFormats.split("|");
18833             }
18834             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18835                 v = Date.parseDate(value, this.altFormatsArray[i]);
18836             }
18837         }
18838         return v;
18839     },
18840     
18841     formatDate : function(date, fmt)
18842     {   
18843         return (!date || !(date instanceof Date)) ?
18844         date : date.dateFormat(fmt || this.format);
18845     },
18846     
18847     onFocus : function()
18848     {
18849         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18850         this.showPopup();
18851     },
18852     
18853     onBlur : function()
18854     {
18855         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18856         
18857         var d = this.inputEl().getValue();
18858         
18859         this.setValue(d);
18860                 
18861         this.hidePopup();
18862     },
18863     
18864     showPopup : function()
18865     {
18866         this.picker().show();
18867         this.update();
18868         this.place();
18869         
18870         this.fireEvent('showpopup', this, this.date);
18871     },
18872     
18873     hidePopup : function()
18874     {
18875         if(this.isInline) {
18876             return;
18877         }
18878         this.picker().hide();
18879         this.viewMode = this.startViewMode;
18880         this.showMode();
18881         
18882         this.fireEvent('hidepopup', this, this.date);
18883         
18884     },
18885     
18886     onMousedown: function(e)
18887     {
18888         e.stopPropagation();
18889         e.preventDefault();
18890     },
18891     
18892     keyup: function(e)
18893     {
18894         Roo.bootstrap.DateField.superclass.keyup.call(this);
18895         this.update();
18896     },
18897
18898     setValue: function(v)
18899     {
18900         if(this.fireEvent('beforeselect', this, v) !== false){
18901             var d = new Date(this.parseDate(v) ).clearTime();
18902         
18903             if(isNaN(d.getTime())){
18904                 this.date = this.viewDate = '';
18905                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18906                 return;
18907             }
18908
18909             v = this.formatDate(d);
18910
18911             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18912
18913             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18914
18915             this.update();
18916
18917             this.fireEvent('select', this, this.date);
18918         }
18919     },
18920     
18921     getValue: function()
18922     {
18923         return this.formatDate(this.date);
18924     },
18925     
18926     fireKey: function(e)
18927     {
18928         if (!this.picker().isVisible()){
18929             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18930                 this.showPopup();
18931             }
18932             return;
18933         }
18934         
18935         var dateChanged = false,
18936         dir, day, month,
18937         newDate, newViewDate;
18938         
18939         switch(e.keyCode){
18940             case 27: // escape
18941                 this.hidePopup();
18942                 e.preventDefault();
18943                 break;
18944             case 37: // left
18945             case 39: // right
18946                 if (!this.keyboardNavigation) {
18947                     break;
18948                 }
18949                 dir = e.keyCode == 37 ? -1 : 1;
18950                 
18951                 if (e.ctrlKey){
18952                     newDate = this.moveYear(this.date, dir);
18953                     newViewDate = this.moveYear(this.viewDate, dir);
18954                 } else if (e.shiftKey){
18955                     newDate = this.moveMonth(this.date, dir);
18956                     newViewDate = this.moveMonth(this.viewDate, dir);
18957                 } else {
18958                     newDate = new Date(this.date);
18959                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18960                     newViewDate = new Date(this.viewDate);
18961                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18962                 }
18963                 if (this.dateWithinRange(newDate)){
18964                     this.date = newDate;
18965                     this.viewDate = newViewDate;
18966                     this.setValue(this.formatDate(this.date));
18967 //                    this.update();
18968                     e.preventDefault();
18969                     dateChanged = true;
18970                 }
18971                 break;
18972             case 38: // up
18973             case 40: // down
18974                 if (!this.keyboardNavigation) {
18975                     break;
18976                 }
18977                 dir = e.keyCode == 38 ? -1 : 1;
18978                 if (e.ctrlKey){
18979                     newDate = this.moveYear(this.date, dir);
18980                     newViewDate = this.moveYear(this.viewDate, dir);
18981                 } else if (e.shiftKey){
18982                     newDate = this.moveMonth(this.date, dir);
18983                     newViewDate = this.moveMonth(this.viewDate, dir);
18984                 } else {
18985                     newDate = new Date(this.date);
18986                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18987                     newViewDate = new Date(this.viewDate);
18988                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18989                 }
18990                 if (this.dateWithinRange(newDate)){
18991                     this.date = newDate;
18992                     this.viewDate = newViewDate;
18993                     this.setValue(this.formatDate(this.date));
18994 //                    this.update();
18995                     e.preventDefault();
18996                     dateChanged = true;
18997                 }
18998                 break;
18999             case 13: // enter
19000                 this.setValue(this.formatDate(this.date));
19001                 this.hidePopup();
19002                 e.preventDefault();
19003                 break;
19004             case 9: // tab
19005                 this.setValue(this.formatDate(this.date));
19006                 this.hidePopup();
19007                 break;
19008             case 16: // shift
19009             case 17: // ctrl
19010             case 18: // alt
19011                 break;
19012             default :
19013                 this.hide();
19014                 
19015         }
19016     },
19017     
19018     
19019     onClick: function(e) 
19020     {
19021         e.stopPropagation();
19022         e.preventDefault();
19023         
19024         var target = e.getTarget();
19025         
19026         if(target.nodeName.toLowerCase() === 'i'){
19027             target = Roo.get(target).dom.parentNode;
19028         }
19029         
19030         var nodeName = target.nodeName;
19031         var className = target.className;
19032         var html = target.innerHTML;
19033         //Roo.log(nodeName);
19034         
19035         switch(nodeName.toLowerCase()) {
19036             case 'th':
19037                 switch(className) {
19038                     case 'switch':
19039                         this.showMode(1);
19040                         break;
19041                     case 'prev':
19042                     case 'next':
19043                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19044                         switch(this.viewMode){
19045                                 case 0:
19046                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19047                                         break;
19048                                 case 1:
19049                                 case 2:
19050                                         this.viewDate = this.moveYear(this.viewDate, dir);
19051                                         break;
19052                         }
19053                         this.fill();
19054                         break;
19055                     case 'today':
19056                         var date = new Date();
19057                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19058 //                        this.fill()
19059                         this.setValue(this.formatDate(this.date));
19060                         
19061                         this.hidePopup();
19062                         break;
19063                 }
19064                 break;
19065             case 'span':
19066                 if (className.indexOf('disabled') < 0) {
19067                     this.viewDate.setUTCDate(1);
19068                     if (className.indexOf('month') > -1) {
19069                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19070                     } else {
19071                         var year = parseInt(html, 10) || 0;
19072                         this.viewDate.setUTCFullYear(year);
19073                         
19074                     }
19075                     
19076                     if(this.singleMode){
19077                         this.setValue(this.formatDate(this.viewDate));
19078                         this.hidePopup();
19079                         return;
19080                     }
19081                     
19082                     this.showMode(-1);
19083                     this.fill();
19084                 }
19085                 break;
19086                 
19087             case 'td':
19088                 //Roo.log(className);
19089                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19090                     var day = parseInt(html, 10) || 1;
19091                     var year = this.viewDate.getUTCFullYear(),
19092                         month = this.viewDate.getUTCMonth();
19093
19094                     if (className.indexOf('old') > -1) {
19095                         if(month === 0 ){
19096                             month = 11;
19097                             year -= 1;
19098                         }else{
19099                             month -= 1;
19100                         }
19101                     } else if (className.indexOf('new') > -1) {
19102                         if (month == 11) {
19103                             month = 0;
19104                             year += 1;
19105                         } else {
19106                             month += 1;
19107                         }
19108                     }
19109                     //Roo.log([year,month,day]);
19110                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19111                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19112 //                    this.fill();
19113                     //Roo.log(this.formatDate(this.date));
19114                     this.setValue(this.formatDate(this.date));
19115                     this.hidePopup();
19116                 }
19117                 break;
19118         }
19119     },
19120     
19121     setStartDate: function(startDate)
19122     {
19123         this.startDate = startDate || -Infinity;
19124         if (this.startDate !== -Infinity) {
19125             this.startDate = this.parseDate(this.startDate);
19126         }
19127         this.update();
19128         this.updateNavArrows();
19129     },
19130
19131     setEndDate: function(endDate)
19132     {
19133         this.endDate = endDate || Infinity;
19134         if (this.endDate !== Infinity) {
19135             this.endDate = this.parseDate(this.endDate);
19136         }
19137         this.update();
19138         this.updateNavArrows();
19139     },
19140     
19141     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19142     {
19143         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19144         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19145             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19146         }
19147         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19148             return parseInt(d, 10);
19149         });
19150         this.update();
19151         this.updateNavArrows();
19152     },
19153     
19154     updateNavArrows: function() 
19155     {
19156         if(this.singleMode){
19157             return;
19158         }
19159         
19160         var d = new Date(this.viewDate),
19161         year = d.getUTCFullYear(),
19162         month = d.getUTCMonth();
19163         
19164         Roo.each(this.picker().select('.prev', true).elements, function(v){
19165             v.show();
19166             switch (this.viewMode) {
19167                 case 0:
19168
19169                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19170                         v.hide();
19171                     }
19172                     break;
19173                 case 1:
19174                 case 2:
19175                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19176                         v.hide();
19177                     }
19178                     break;
19179             }
19180         });
19181         
19182         Roo.each(this.picker().select('.next', true).elements, function(v){
19183             v.show();
19184             switch (this.viewMode) {
19185                 case 0:
19186
19187                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19188                         v.hide();
19189                     }
19190                     break;
19191                 case 1:
19192                 case 2:
19193                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19194                         v.hide();
19195                     }
19196                     break;
19197             }
19198         })
19199     },
19200     
19201     moveMonth: function(date, dir)
19202     {
19203         if (!dir) {
19204             return date;
19205         }
19206         var new_date = new Date(date.valueOf()),
19207         day = new_date.getUTCDate(),
19208         month = new_date.getUTCMonth(),
19209         mag = Math.abs(dir),
19210         new_month, test;
19211         dir = dir > 0 ? 1 : -1;
19212         if (mag == 1){
19213             test = dir == -1
19214             // If going back one month, make sure month is not current month
19215             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19216             ? function(){
19217                 return new_date.getUTCMonth() == month;
19218             }
19219             // If going forward one month, make sure month is as expected
19220             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19221             : function(){
19222                 return new_date.getUTCMonth() != new_month;
19223             };
19224             new_month = month + dir;
19225             new_date.setUTCMonth(new_month);
19226             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19227             if (new_month < 0 || new_month > 11) {
19228                 new_month = (new_month + 12) % 12;
19229             }
19230         } else {
19231             // For magnitudes >1, move one month at a time...
19232             for (var i=0; i<mag; i++) {
19233                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19234                 new_date = this.moveMonth(new_date, dir);
19235             }
19236             // ...then reset the day, keeping it in the new month
19237             new_month = new_date.getUTCMonth();
19238             new_date.setUTCDate(day);
19239             test = function(){
19240                 return new_month != new_date.getUTCMonth();
19241             };
19242         }
19243         // Common date-resetting loop -- if date is beyond end of month, make it
19244         // end of month
19245         while (test()){
19246             new_date.setUTCDate(--day);
19247             new_date.setUTCMonth(new_month);
19248         }
19249         return new_date;
19250     },
19251
19252     moveYear: function(date, dir)
19253     {
19254         return this.moveMonth(date, dir*12);
19255     },
19256
19257     dateWithinRange: function(date)
19258     {
19259         return date >= this.startDate && date <= this.endDate;
19260     },
19261
19262     
19263     remove: function() 
19264     {
19265         this.picker().remove();
19266     },
19267     
19268     validateValue : function(value)
19269     {
19270         if(this.getVisibilityEl().hasClass('hidden')){
19271             return true;
19272         }
19273         
19274         if(value.length < 1)  {
19275             if(this.allowBlank){
19276                 return true;
19277             }
19278             return false;
19279         }
19280         
19281         if(value.length < this.minLength){
19282             return false;
19283         }
19284         if(value.length > this.maxLength){
19285             return false;
19286         }
19287         if(this.vtype){
19288             var vt = Roo.form.VTypes;
19289             if(!vt[this.vtype](value, this)){
19290                 return false;
19291             }
19292         }
19293         if(typeof this.validator == "function"){
19294             var msg = this.validator(value);
19295             if(msg !== true){
19296                 return false;
19297             }
19298         }
19299         
19300         if(this.regex && !this.regex.test(value)){
19301             return false;
19302         }
19303         
19304         if(typeof(this.parseDate(value)) == 'undefined'){
19305             return false;
19306         }
19307         
19308         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19309             return false;
19310         }      
19311         
19312         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19313             return false;
19314         } 
19315         
19316         
19317         return true;
19318     },
19319     
19320     reset : function()
19321     {
19322         this.date = this.viewDate = '';
19323         
19324         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19325     }
19326    
19327 });
19328
19329 Roo.apply(Roo.bootstrap.DateField,  {
19330     
19331     head : {
19332         tag: 'thead',
19333         cn: [
19334         {
19335             tag: 'tr',
19336             cn: [
19337             {
19338                 tag: 'th',
19339                 cls: 'prev',
19340                 html: '<i class="fa fa-arrow-left"/>'
19341             },
19342             {
19343                 tag: 'th',
19344                 cls: 'switch',
19345                 colspan: '5'
19346             },
19347             {
19348                 tag: 'th',
19349                 cls: 'next',
19350                 html: '<i class="fa fa-arrow-right"/>'
19351             }
19352
19353             ]
19354         }
19355         ]
19356     },
19357     
19358     content : {
19359         tag: 'tbody',
19360         cn: [
19361         {
19362             tag: 'tr',
19363             cn: [
19364             {
19365                 tag: 'td',
19366                 colspan: '7'
19367             }
19368             ]
19369         }
19370         ]
19371     },
19372     
19373     footer : {
19374         tag: 'tfoot',
19375         cn: [
19376         {
19377             tag: 'tr',
19378             cn: [
19379             {
19380                 tag: 'th',
19381                 colspan: '7',
19382                 cls: 'today'
19383             }
19384                     
19385             ]
19386         }
19387         ]
19388     },
19389     
19390     dates:{
19391         en: {
19392             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19393             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19394             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19395             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19396             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19397             today: "Today"
19398         }
19399     },
19400     
19401     modes: [
19402     {
19403         clsName: 'days',
19404         navFnc: 'Month',
19405         navStep: 1
19406     },
19407     {
19408         clsName: 'months',
19409         navFnc: 'FullYear',
19410         navStep: 1
19411     },
19412     {
19413         clsName: 'years',
19414         navFnc: 'FullYear',
19415         navStep: 10
19416     }]
19417 });
19418
19419 Roo.apply(Roo.bootstrap.DateField,  {
19420   
19421     template : {
19422         tag: 'div',
19423         cls: 'datepicker dropdown-menu roo-dynamic',
19424         cn: [
19425         {
19426             tag: 'div',
19427             cls: 'datepicker-days',
19428             cn: [
19429             {
19430                 tag: 'table',
19431                 cls: 'table-condensed',
19432                 cn:[
19433                 Roo.bootstrap.DateField.head,
19434                 {
19435                     tag: 'tbody'
19436                 },
19437                 Roo.bootstrap.DateField.footer
19438                 ]
19439             }
19440             ]
19441         },
19442         {
19443             tag: 'div',
19444             cls: 'datepicker-months',
19445             cn: [
19446             {
19447                 tag: 'table',
19448                 cls: 'table-condensed',
19449                 cn:[
19450                 Roo.bootstrap.DateField.head,
19451                 Roo.bootstrap.DateField.content,
19452                 Roo.bootstrap.DateField.footer
19453                 ]
19454             }
19455             ]
19456         },
19457         {
19458             tag: 'div',
19459             cls: 'datepicker-years',
19460             cn: [
19461             {
19462                 tag: 'table',
19463                 cls: 'table-condensed',
19464                 cn:[
19465                 Roo.bootstrap.DateField.head,
19466                 Roo.bootstrap.DateField.content,
19467                 Roo.bootstrap.DateField.footer
19468                 ]
19469             }
19470             ]
19471         }
19472         ]
19473     }
19474 });
19475
19476  
19477
19478  /*
19479  * - LGPL
19480  *
19481  * TimeField
19482  * 
19483  */
19484
19485 /**
19486  * @class Roo.bootstrap.TimeField
19487  * @extends Roo.bootstrap.Input
19488  * Bootstrap DateField class
19489  * 
19490  * 
19491  * @constructor
19492  * Create a new TimeField
19493  * @param {Object} config The config object
19494  */
19495
19496 Roo.bootstrap.TimeField = function(config){
19497     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19498     this.addEvents({
19499             /**
19500              * @event show
19501              * Fires when this field show.
19502              * @param {Roo.bootstrap.DateField} thisthis
19503              * @param {Mixed} date The date value
19504              */
19505             show : true,
19506             /**
19507              * @event show
19508              * Fires when this field hide.
19509              * @param {Roo.bootstrap.DateField} this
19510              * @param {Mixed} date The date value
19511              */
19512             hide : true,
19513             /**
19514              * @event select
19515              * Fires when select a date.
19516              * @param {Roo.bootstrap.DateField} this
19517              * @param {Mixed} date The date value
19518              */
19519             select : true
19520         });
19521 };
19522
19523 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19524     
19525     /**
19526      * @cfg {String} format
19527      * The default time format string which can be overriden for localization support.  The format must be
19528      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19529      */
19530     format : "H:i",
19531        
19532     onRender: function(ct, position)
19533     {
19534         
19535         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19536                 
19537         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19538         
19539         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19540         
19541         this.pop = this.picker().select('>.datepicker-time',true).first();
19542         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19543         
19544         this.picker().on('mousedown', this.onMousedown, this);
19545         this.picker().on('click', this.onClick, this);
19546         
19547         this.picker().addClass('datepicker-dropdown');
19548     
19549         this.fillTime();
19550         this.update();
19551             
19552         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19553         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19554         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19555         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19556         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19557         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19558
19559     },
19560     
19561     fireKey: function(e){
19562         if (!this.picker().isVisible()){
19563             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19564                 this.show();
19565             }
19566             return;
19567         }
19568
19569         e.preventDefault();
19570         
19571         switch(e.keyCode){
19572             case 27: // escape
19573                 this.hide();
19574                 break;
19575             case 37: // left
19576             case 39: // right
19577                 this.onTogglePeriod();
19578                 break;
19579             case 38: // up
19580                 this.onIncrementMinutes();
19581                 break;
19582             case 40: // down
19583                 this.onDecrementMinutes();
19584                 break;
19585             case 13: // enter
19586             case 9: // tab
19587                 this.setTime();
19588                 break;
19589         }
19590     },
19591     
19592     onClick: function(e) {
19593         e.stopPropagation();
19594         e.preventDefault();
19595     },
19596     
19597     picker : function()
19598     {
19599         return this.el.select('.datepicker', true).first();
19600     },
19601     
19602     fillTime: function()
19603     {    
19604         var time = this.pop.select('tbody', true).first();
19605         
19606         time.dom.innerHTML = '';
19607         
19608         time.createChild({
19609             tag: 'tr',
19610             cn: [
19611                 {
19612                     tag: 'td',
19613                     cn: [
19614                         {
19615                             tag: 'a',
19616                             href: '#',
19617                             cls: 'btn',
19618                             cn: [
19619                                 {
19620                                     tag: 'span',
19621                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19622                                 }
19623                             ]
19624                         } 
19625                     ]
19626                 },
19627                 {
19628                     tag: 'td',
19629                     cls: 'separator'
19630                 },
19631                 {
19632                     tag: 'td',
19633                     cn: [
19634                         {
19635                             tag: 'a',
19636                             href: '#',
19637                             cls: 'btn',
19638                             cn: [
19639                                 {
19640                                     tag: 'span',
19641                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19642                                 }
19643                             ]
19644                         }
19645                     ]
19646                 },
19647                 {
19648                     tag: 'td',
19649                     cls: 'separator'
19650                 }
19651             ]
19652         });
19653         
19654         time.createChild({
19655             tag: 'tr',
19656             cn: [
19657                 {
19658                     tag: 'td',
19659                     cn: [
19660                         {
19661                             tag: 'span',
19662                             cls: 'timepicker-hour',
19663                             html: '00'
19664                         }  
19665                     ]
19666                 },
19667                 {
19668                     tag: 'td',
19669                     cls: 'separator',
19670                     html: ':'
19671                 },
19672                 {
19673                     tag: 'td',
19674                     cn: [
19675                         {
19676                             tag: 'span',
19677                             cls: 'timepicker-minute',
19678                             html: '00'
19679                         }  
19680                     ]
19681                 },
19682                 {
19683                     tag: 'td',
19684                     cls: 'separator'
19685                 },
19686                 {
19687                     tag: 'td',
19688                     cn: [
19689                         {
19690                             tag: 'button',
19691                             type: 'button',
19692                             cls: 'btn btn-primary period',
19693                             html: 'AM'
19694                             
19695                         }
19696                     ]
19697                 }
19698             ]
19699         });
19700         
19701         time.createChild({
19702             tag: 'tr',
19703             cn: [
19704                 {
19705                     tag: 'td',
19706                     cn: [
19707                         {
19708                             tag: 'a',
19709                             href: '#',
19710                             cls: 'btn',
19711                             cn: [
19712                                 {
19713                                     tag: 'span',
19714                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19715                                 }
19716                             ]
19717                         }
19718                     ]
19719                 },
19720                 {
19721                     tag: 'td',
19722                     cls: 'separator'
19723                 },
19724                 {
19725                     tag: 'td',
19726                     cn: [
19727                         {
19728                             tag: 'a',
19729                             href: '#',
19730                             cls: 'btn',
19731                             cn: [
19732                                 {
19733                                     tag: 'span',
19734                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19735                                 }
19736                             ]
19737                         }
19738                     ]
19739                 },
19740                 {
19741                     tag: 'td',
19742                     cls: 'separator'
19743                 }
19744             ]
19745         });
19746         
19747     },
19748     
19749     update: function()
19750     {
19751         
19752         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19753         
19754         this.fill();
19755     },
19756     
19757     fill: function() 
19758     {
19759         var hours = this.time.getHours();
19760         var minutes = this.time.getMinutes();
19761         var period = 'AM';
19762         
19763         if(hours > 11){
19764             period = 'PM';
19765         }
19766         
19767         if(hours == 0){
19768             hours = 12;
19769         }
19770         
19771         
19772         if(hours > 12){
19773             hours = hours - 12;
19774         }
19775         
19776         if(hours < 10){
19777             hours = '0' + hours;
19778         }
19779         
19780         if(minutes < 10){
19781             minutes = '0' + minutes;
19782         }
19783         
19784         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19785         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19786         this.pop.select('button', true).first().dom.innerHTML = period;
19787         
19788     },
19789     
19790     place: function()
19791     {   
19792         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19793         
19794         var cls = ['bottom'];
19795         
19796         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19797             cls.pop();
19798             cls.push('top');
19799         }
19800         
19801         cls.push('right');
19802         
19803         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19804             cls.pop();
19805             cls.push('left');
19806         }
19807         
19808         this.picker().addClass(cls.join('-'));
19809         
19810         var _this = this;
19811         
19812         Roo.each(cls, function(c){
19813             if(c == 'bottom'){
19814                 _this.picker().setTop(_this.inputEl().getHeight());
19815                 return;
19816             }
19817             if(c == 'top'){
19818                 _this.picker().setTop(0 - _this.picker().getHeight());
19819                 return;
19820             }
19821             
19822             if(c == 'left'){
19823                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19824                 return;
19825             }
19826             if(c == 'right'){
19827                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19828                 return;
19829             }
19830         });
19831         
19832     },
19833   
19834     onFocus : function()
19835     {
19836         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19837         this.show();
19838     },
19839     
19840     onBlur : function()
19841     {
19842         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19843         this.hide();
19844     },
19845     
19846     show : function()
19847     {
19848         this.picker().show();
19849         this.pop.show();
19850         this.update();
19851         this.place();
19852         
19853         this.fireEvent('show', this, this.date);
19854     },
19855     
19856     hide : function()
19857     {
19858         this.picker().hide();
19859         this.pop.hide();
19860         
19861         this.fireEvent('hide', this, this.date);
19862     },
19863     
19864     setTime : function()
19865     {
19866         this.hide();
19867         this.setValue(this.time.format(this.format));
19868         
19869         this.fireEvent('select', this, this.date);
19870         
19871         
19872     },
19873     
19874     onMousedown: function(e){
19875         e.stopPropagation();
19876         e.preventDefault();
19877     },
19878     
19879     onIncrementHours: function()
19880     {
19881         Roo.log('onIncrementHours');
19882         this.time = this.time.add(Date.HOUR, 1);
19883         this.update();
19884         
19885     },
19886     
19887     onDecrementHours: function()
19888     {
19889         Roo.log('onDecrementHours');
19890         this.time = this.time.add(Date.HOUR, -1);
19891         this.update();
19892     },
19893     
19894     onIncrementMinutes: function()
19895     {
19896         Roo.log('onIncrementMinutes');
19897         this.time = this.time.add(Date.MINUTE, 1);
19898         this.update();
19899     },
19900     
19901     onDecrementMinutes: function()
19902     {
19903         Roo.log('onDecrementMinutes');
19904         this.time = this.time.add(Date.MINUTE, -1);
19905         this.update();
19906     },
19907     
19908     onTogglePeriod: function()
19909     {
19910         Roo.log('onTogglePeriod');
19911         this.time = this.time.add(Date.HOUR, 12);
19912         this.update();
19913     }
19914     
19915    
19916 });
19917
19918 Roo.apply(Roo.bootstrap.TimeField,  {
19919     
19920     content : {
19921         tag: 'tbody',
19922         cn: [
19923             {
19924                 tag: 'tr',
19925                 cn: [
19926                 {
19927                     tag: 'td',
19928                     colspan: '7'
19929                 }
19930                 ]
19931             }
19932         ]
19933     },
19934     
19935     footer : {
19936         tag: 'tfoot',
19937         cn: [
19938             {
19939                 tag: 'tr',
19940                 cn: [
19941                 {
19942                     tag: 'th',
19943                     colspan: '7',
19944                     cls: '',
19945                     cn: [
19946                         {
19947                             tag: 'button',
19948                             cls: 'btn btn-info ok',
19949                             html: 'OK'
19950                         }
19951                     ]
19952                 }
19953
19954                 ]
19955             }
19956         ]
19957     }
19958 });
19959
19960 Roo.apply(Roo.bootstrap.TimeField,  {
19961   
19962     template : {
19963         tag: 'div',
19964         cls: 'datepicker dropdown-menu',
19965         cn: [
19966             {
19967                 tag: 'div',
19968                 cls: 'datepicker-time',
19969                 cn: [
19970                 {
19971                     tag: 'table',
19972                     cls: 'table-condensed',
19973                     cn:[
19974                     Roo.bootstrap.TimeField.content,
19975                     Roo.bootstrap.TimeField.footer
19976                     ]
19977                 }
19978                 ]
19979             }
19980         ]
19981     }
19982 });
19983
19984  
19985
19986  /*
19987  * - LGPL
19988  *
19989  * MonthField
19990  * 
19991  */
19992
19993 /**
19994  * @class Roo.bootstrap.MonthField
19995  * @extends Roo.bootstrap.Input
19996  * Bootstrap MonthField class
19997  * 
19998  * @cfg {String} language default en
19999  * 
20000  * @constructor
20001  * Create a new MonthField
20002  * @param {Object} config The config object
20003  */
20004
20005 Roo.bootstrap.MonthField = function(config){
20006     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20007     
20008     this.addEvents({
20009         /**
20010          * @event show
20011          * Fires when this field show.
20012          * @param {Roo.bootstrap.MonthField} this
20013          * @param {Mixed} date The date value
20014          */
20015         show : true,
20016         /**
20017          * @event show
20018          * Fires when this field hide.
20019          * @param {Roo.bootstrap.MonthField} this
20020          * @param {Mixed} date The date value
20021          */
20022         hide : true,
20023         /**
20024          * @event select
20025          * Fires when select a date.
20026          * @param {Roo.bootstrap.MonthField} this
20027          * @param {String} oldvalue The old value
20028          * @param {String} newvalue The new value
20029          */
20030         select : true
20031     });
20032 };
20033
20034 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20035     
20036     onRender: function(ct, position)
20037     {
20038         
20039         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20040         
20041         this.language = this.language || 'en';
20042         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20043         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20044         
20045         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20046         this.isInline = false;
20047         this.isInput = true;
20048         this.component = this.el.select('.add-on', true).first() || false;
20049         this.component = (this.component && this.component.length === 0) ? false : this.component;
20050         this.hasInput = this.component && this.inputEL().length;
20051         
20052         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20053         
20054         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20055         
20056         this.picker().on('mousedown', this.onMousedown, this);
20057         this.picker().on('click', this.onClick, this);
20058         
20059         this.picker().addClass('datepicker-dropdown');
20060         
20061         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20062             v.setStyle('width', '189px');
20063         });
20064         
20065         this.fillMonths();
20066         
20067         this.update();
20068         
20069         if(this.isInline) {
20070             this.show();
20071         }
20072         
20073     },
20074     
20075     setValue: function(v, suppressEvent)
20076     {   
20077         var o = this.getValue();
20078         
20079         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20080         
20081         this.update();
20082
20083         if(suppressEvent !== true){
20084             this.fireEvent('select', this, o, v);
20085         }
20086         
20087     },
20088     
20089     getValue: function()
20090     {
20091         return this.value;
20092     },
20093     
20094     onClick: function(e) 
20095     {
20096         e.stopPropagation();
20097         e.preventDefault();
20098         
20099         var target = e.getTarget();
20100         
20101         if(target.nodeName.toLowerCase() === 'i'){
20102             target = Roo.get(target).dom.parentNode;
20103         }
20104         
20105         var nodeName = target.nodeName;
20106         var className = target.className;
20107         var html = target.innerHTML;
20108         
20109         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20110             return;
20111         }
20112         
20113         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20114         
20115         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20116         
20117         this.hide();
20118                         
20119     },
20120     
20121     picker : function()
20122     {
20123         return this.pickerEl;
20124     },
20125     
20126     fillMonths: function()
20127     {    
20128         var i = 0;
20129         var months = this.picker().select('>.datepicker-months td', true).first();
20130         
20131         months.dom.innerHTML = '';
20132         
20133         while (i < 12) {
20134             var month = {
20135                 tag: 'span',
20136                 cls: 'month',
20137                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20138             };
20139             
20140             months.createChild(month);
20141         }
20142         
20143     },
20144     
20145     update: function()
20146     {
20147         var _this = this;
20148         
20149         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20150             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20151         }
20152         
20153         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20154             e.removeClass('active');
20155             
20156             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20157                 e.addClass('active');
20158             }
20159         })
20160     },
20161     
20162     place: function()
20163     {
20164         if(this.isInline) {
20165             return;
20166         }
20167         
20168         this.picker().removeClass(['bottom', 'top']);
20169         
20170         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20171             /*
20172              * place to the top of element!
20173              *
20174              */
20175             
20176             this.picker().addClass('top');
20177             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20178             
20179             return;
20180         }
20181         
20182         this.picker().addClass('bottom');
20183         
20184         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20185     },
20186     
20187     onFocus : function()
20188     {
20189         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20190         this.show();
20191     },
20192     
20193     onBlur : function()
20194     {
20195         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20196         
20197         var d = this.inputEl().getValue();
20198         
20199         this.setValue(d);
20200                 
20201         this.hide();
20202     },
20203     
20204     show : function()
20205     {
20206         this.picker().show();
20207         this.picker().select('>.datepicker-months', true).first().show();
20208         this.update();
20209         this.place();
20210         
20211         this.fireEvent('show', this, this.date);
20212     },
20213     
20214     hide : function()
20215     {
20216         if(this.isInline) {
20217             return;
20218         }
20219         this.picker().hide();
20220         this.fireEvent('hide', this, this.date);
20221         
20222     },
20223     
20224     onMousedown: function(e)
20225     {
20226         e.stopPropagation();
20227         e.preventDefault();
20228     },
20229     
20230     keyup: function(e)
20231     {
20232         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20233         this.update();
20234     },
20235
20236     fireKey: function(e)
20237     {
20238         if (!this.picker().isVisible()){
20239             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20240                 this.show();
20241             }
20242             return;
20243         }
20244         
20245         var dir;
20246         
20247         switch(e.keyCode){
20248             case 27: // escape
20249                 this.hide();
20250                 e.preventDefault();
20251                 break;
20252             case 37: // left
20253             case 39: // right
20254                 dir = e.keyCode == 37 ? -1 : 1;
20255                 
20256                 this.vIndex = this.vIndex + dir;
20257                 
20258                 if(this.vIndex < 0){
20259                     this.vIndex = 0;
20260                 }
20261                 
20262                 if(this.vIndex > 11){
20263                     this.vIndex = 11;
20264                 }
20265                 
20266                 if(isNaN(this.vIndex)){
20267                     this.vIndex = 0;
20268                 }
20269                 
20270                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20271                 
20272                 break;
20273             case 38: // up
20274             case 40: // down
20275                 
20276                 dir = e.keyCode == 38 ? -1 : 1;
20277                 
20278                 this.vIndex = this.vIndex + dir * 4;
20279                 
20280                 if(this.vIndex < 0){
20281                     this.vIndex = 0;
20282                 }
20283                 
20284                 if(this.vIndex > 11){
20285                     this.vIndex = 11;
20286                 }
20287                 
20288                 if(isNaN(this.vIndex)){
20289                     this.vIndex = 0;
20290                 }
20291                 
20292                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20293                 break;
20294                 
20295             case 13: // enter
20296                 
20297                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20298                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20299                 }
20300                 
20301                 this.hide();
20302                 e.preventDefault();
20303                 break;
20304             case 9: // tab
20305                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20306                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20307                 }
20308                 this.hide();
20309                 break;
20310             case 16: // shift
20311             case 17: // ctrl
20312             case 18: // alt
20313                 break;
20314             default :
20315                 this.hide();
20316                 
20317         }
20318     },
20319     
20320     remove: function() 
20321     {
20322         this.picker().remove();
20323     }
20324    
20325 });
20326
20327 Roo.apply(Roo.bootstrap.MonthField,  {
20328     
20329     content : {
20330         tag: 'tbody',
20331         cn: [
20332         {
20333             tag: 'tr',
20334             cn: [
20335             {
20336                 tag: 'td',
20337                 colspan: '7'
20338             }
20339             ]
20340         }
20341         ]
20342     },
20343     
20344     dates:{
20345         en: {
20346             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20347             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20348         }
20349     }
20350 });
20351
20352 Roo.apply(Roo.bootstrap.MonthField,  {
20353   
20354     template : {
20355         tag: 'div',
20356         cls: 'datepicker dropdown-menu roo-dynamic',
20357         cn: [
20358             {
20359                 tag: 'div',
20360                 cls: 'datepicker-months',
20361                 cn: [
20362                 {
20363                     tag: 'table',
20364                     cls: 'table-condensed',
20365                     cn:[
20366                         Roo.bootstrap.DateField.content
20367                     ]
20368                 }
20369                 ]
20370             }
20371         ]
20372     }
20373 });
20374
20375  
20376
20377  
20378  /*
20379  * - LGPL
20380  *
20381  * CheckBox
20382  * 
20383  */
20384
20385 /**
20386  * @class Roo.bootstrap.CheckBox
20387  * @extends Roo.bootstrap.Input
20388  * Bootstrap CheckBox class
20389  * 
20390  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20391  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20392  * @cfg {String} boxLabel The text that appears beside the checkbox
20393  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20394  * @cfg {Boolean} checked initnal the element
20395  * @cfg {Boolean} inline inline the element (default false)
20396  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20397  * @cfg {String} tooltip label tooltip
20398  * 
20399  * @constructor
20400  * Create a new CheckBox
20401  * @param {Object} config The config object
20402  */
20403
20404 Roo.bootstrap.CheckBox = function(config){
20405     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20406    
20407     this.addEvents({
20408         /**
20409         * @event check
20410         * Fires when the element is checked or unchecked.
20411         * @param {Roo.bootstrap.CheckBox} this This input
20412         * @param {Boolean} checked The new checked value
20413         */
20414        check : true,
20415        /**
20416         * @event click
20417         * Fires when the element is click.
20418         * @param {Roo.bootstrap.CheckBox} this This input
20419         */
20420        click : true
20421     });
20422     
20423 };
20424
20425 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20426   
20427     inputType: 'checkbox',
20428     inputValue: 1,
20429     valueOff: 0,
20430     boxLabel: false,
20431     checked: false,
20432     weight : false,
20433     inline: false,
20434     tooltip : '',
20435     
20436     getAutoCreate : function()
20437     {
20438         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20439         
20440         var id = Roo.id();
20441         
20442         var cfg = {};
20443         
20444         cfg.cls = 'form-group ' + this.inputType; //input-group
20445         
20446         if(this.inline){
20447             cfg.cls += ' ' + this.inputType + '-inline';
20448         }
20449         
20450         var input =  {
20451             tag: 'input',
20452             id : id,
20453             type : this.inputType,
20454             value : this.inputValue,
20455             cls : 'roo-' + this.inputType, //'form-box',
20456             placeholder : this.placeholder || ''
20457             
20458         };
20459         
20460         if(this.inputType != 'radio'){
20461             var hidden =  {
20462                 tag: 'input',
20463                 type : 'hidden',
20464                 cls : 'roo-hidden-value',
20465                 value : this.checked ? this.inputValue : this.valueOff
20466             };
20467         }
20468         
20469             
20470         if (this.weight) { // Validity check?
20471             cfg.cls += " " + this.inputType + "-" + this.weight;
20472         }
20473         
20474         if (this.disabled) {
20475             input.disabled=true;
20476         }
20477         
20478         if(this.checked){
20479             input.checked = this.checked;
20480         }
20481         
20482         if (this.name) {
20483             
20484             input.name = this.name;
20485             
20486             if(this.inputType != 'radio'){
20487                 hidden.name = this.name;
20488                 input.name = '_hidden_' + this.name;
20489             }
20490         }
20491         
20492         if (this.size) {
20493             input.cls += ' input-' + this.size;
20494         }
20495         
20496         var settings=this;
20497         
20498         ['xs','sm','md','lg'].map(function(size){
20499             if (settings[size]) {
20500                 cfg.cls += ' col-' + size + '-' + settings[size];
20501             }
20502         });
20503         
20504         var inputblock = input;
20505          
20506         if (this.before || this.after) {
20507             
20508             inputblock = {
20509                 cls : 'input-group',
20510                 cn :  [] 
20511             };
20512             
20513             if (this.before) {
20514                 inputblock.cn.push({
20515                     tag :'span',
20516                     cls : 'input-group-addon',
20517                     html : this.before
20518                 });
20519             }
20520             
20521             inputblock.cn.push(input);
20522             
20523             if(this.inputType != 'radio'){
20524                 inputblock.cn.push(hidden);
20525             }
20526             
20527             if (this.after) {
20528                 inputblock.cn.push({
20529                     tag :'span',
20530                     cls : 'input-group-addon',
20531                     html : this.after
20532                 });
20533             }
20534             
20535         }
20536         
20537         if (align ==='left' && this.fieldLabel.length) {
20538 //                Roo.log("left and has label");
20539             cfg.cn = [
20540                 {
20541                     tag: 'label',
20542                     'for' :  id,
20543                     cls : 'control-label',
20544                     html : this.fieldLabel
20545                 },
20546                 {
20547                     cls : "", 
20548                     cn: [
20549                         inputblock
20550                     ]
20551                 }
20552             ];
20553             
20554             if(this.labelWidth > 12){
20555                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20556             }
20557             
20558             if(this.labelWidth < 13 && this.labelmd == 0){
20559                 this.labelmd = this.labelWidth;
20560             }
20561             
20562             if(this.labellg > 0){
20563                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20564                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20565             }
20566             
20567             if(this.labelmd > 0){
20568                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20569                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20570             }
20571             
20572             if(this.labelsm > 0){
20573                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20574                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20575             }
20576             
20577             if(this.labelxs > 0){
20578                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20579                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20580             }
20581             
20582         } else if ( this.fieldLabel.length) {
20583 //                Roo.log(" label");
20584                 cfg.cn = [
20585                    
20586                     {
20587                         tag: this.boxLabel ? 'span' : 'label',
20588                         'for': id,
20589                         cls: 'control-label box-input-label',
20590                         //cls : 'input-group-addon',
20591                         html : this.fieldLabel
20592                     },
20593                     
20594                     inputblock
20595                     
20596                 ];
20597
20598         } else {
20599             
20600 //                Roo.log(" no label && no align");
20601                 cfg.cn = [  inputblock ] ;
20602                 
20603                 
20604         }
20605         
20606         if(this.boxLabel){
20607              var boxLabelCfg = {
20608                 tag: 'label',
20609                 //'for': id, // box label is handled by onclick - so no for...
20610                 cls: 'box-label',
20611                 html: this.boxLabel
20612             };
20613             
20614             if(this.tooltip){
20615                 boxLabelCfg.tooltip = this.tooltip;
20616             }
20617              
20618             cfg.cn.push(boxLabelCfg);
20619         }
20620         
20621         if(this.inputType != 'radio'){
20622             cfg.cn.push(hidden);
20623         }
20624         
20625         return cfg;
20626         
20627     },
20628     
20629     /**
20630      * return the real input element.
20631      */
20632     inputEl: function ()
20633     {
20634         return this.el.select('input.roo-' + this.inputType,true).first();
20635     },
20636     hiddenEl: function ()
20637     {
20638         return this.el.select('input.roo-hidden-value',true).first();
20639     },
20640     
20641     labelEl: function()
20642     {
20643         return this.el.select('label.control-label',true).first();
20644     },
20645     /* depricated... */
20646     
20647     label: function()
20648     {
20649         return this.labelEl();
20650     },
20651     
20652     boxLabelEl: function()
20653     {
20654         return this.el.select('label.box-label',true).first();
20655     },
20656     
20657     initEvents : function()
20658     {
20659 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20660         
20661         this.inputEl().on('click', this.onClick,  this);
20662         
20663         if (this.boxLabel) { 
20664             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20665         }
20666         
20667         this.startValue = this.getValue();
20668         
20669         if(this.groupId){
20670             Roo.bootstrap.CheckBox.register(this);
20671         }
20672     },
20673     
20674     onClick : function(e)
20675     {   
20676         if(this.fireEvent('click', this, e) !== false){
20677             this.setChecked(!this.checked);
20678         }
20679         
20680     },
20681     
20682     setChecked : function(state,suppressEvent)
20683     {
20684         this.startValue = this.getValue();
20685
20686         if(this.inputType == 'radio'){
20687             
20688             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20689                 e.dom.checked = false;
20690             });
20691             
20692             this.inputEl().dom.checked = true;
20693             
20694             this.inputEl().dom.value = this.inputValue;
20695             
20696             if(suppressEvent !== true){
20697                 this.fireEvent('check', this, true);
20698             }
20699             
20700             this.validate();
20701             
20702             return;
20703         }
20704         
20705         this.checked = state;
20706         
20707         this.inputEl().dom.checked = state;
20708         
20709         
20710         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20711         
20712         if(suppressEvent !== true){
20713             this.fireEvent('check', this, state);
20714         }
20715         
20716         this.validate();
20717     },
20718     
20719     getValue : function()
20720     {
20721         if(this.inputType == 'radio'){
20722             return this.getGroupValue();
20723         }
20724         
20725         return this.hiddenEl().dom.value;
20726         
20727     },
20728     
20729     getGroupValue : function()
20730     {
20731         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20732             return '';
20733         }
20734         
20735         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20736     },
20737     
20738     setValue : function(v,suppressEvent)
20739     {
20740         if(this.inputType == 'radio'){
20741             this.setGroupValue(v, suppressEvent);
20742             return;
20743         }
20744         
20745         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20746         
20747         this.validate();
20748     },
20749     
20750     setGroupValue : function(v, suppressEvent)
20751     {
20752         this.startValue = this.getValue();
20753         
20754         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20755             e.dom.checked = false;
20756             
20757             if(e.dom.value == v){
20758                 e.dom.checked = true;
20759             }
20760         });
20761         
20762         if(suppressEvent !== true){
20763             this.fireEvent('check', this, true);
20764         }
20765
20766         this.validate();
20767         
20768         return;
20769     },
20770     
20771     validate : function()
20772     {
20773         if(this.getVisibilityEl().hasClass('hidden')){
20774             return true;
20775         }
20776         
20777         if(
20778                 this.disabled || 
20779                 (this.inputType == 'radio' && this.validateRadio()) ||
20780                 (this.inputType == 'checkbox' && this.validateCheckbox())
20781         ){
20782             this.markValid();
20783             return true;
20784         }
20785         
20786         this.markInvalid();
20787         return false;
20788     },
20789     
20790     validateRadio : function()
20791     {
20792         if(this.getVisibilityEl().hasClass('hidden')){
20793             return true;
20794         }
20795         
20796         if(this.allowBlank){
20797             return true;
20798         }
20799         
20800         var valid = false;
20801         
20802         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20803             if(!e.dom.checked){
20804                 return;
20805             }
20806             
20807             valid = true;
20808             
20809             return false;
20810         });
20811         
20812         return valid;
20813     },
20814     
20815     validateCheckbox : function()
20816     {
20817         if(!this.groupId){
20818             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20819             //return (this.getValue() == this.inputValue) ? true : false;
20820         }
20821         
20822         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20823         
20824         if(!group){
20825             return false;
20826         }
20827         
20828         var r = false;
20829         
20830         for(var i in group){
20831             if(group[i].el.isVisible(true)){
20832                 r = false;
20833                 break;
20834             }
20835             
20836             r = true;
20837         }
20838         
20839         for(var i in group){
20840             if(r){
20841                 break;
20842             }
20843             
20844             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20845         }
20846         
20847         return r;
20848     },
20849     
20850     /**
20851      * Mark this field as valid
20852      */
20853     markValid : function()
20854     {
20855         var _this = this;
20856         
20857         this.fireEvent('valid', this);
20858         
20859         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20860         
20861         if(this.groupId){
20862             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20863         }
20864         
20865         if(label){
20866             label.markValid();
20867         }
20868
20869         if(this.inputType == 'radio'){
20870             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20871                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20872                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20873             });
20874             
20875             return;
20876         }
20877
20878         if(!this.groupId){
20879             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20880             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20881             return;
20882         }
20883         
20884         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20885         
20886         if(!group){
20887             return;
20888         }
20889         
20890         for(var i in group){
20891             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20892             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20893         }
20894     },
20895     
20896      /**
20897      * Mark this field as invalid
20898      * @param {String} msg The validation message
20899      */
20900     markInvalid : function(msg)
20901     {
20902         if(this.allowBlank){
20903             return;
20904         }
20905         
20906         var _this = this;
20907         
20908         this.fireEvent('invalid', this, msg);
20909         
20910         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20911         
20912         if(this.groupId){
20913             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20914         }
20915         
20916         if(label){
20917             label.markInvalid();
20918         }
20919             
20920         if(this.inputType == 'radio'){
20921             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20922                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20923                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20924             });
20925             
20926             return;
20927         }
20928         
20929         if(!this.groupId){
20930             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20931             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20932             return;
20933         }
20934         
20935         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20936         
20937         if(!group){
20938             return;
20939         }
20940         
20941         for(var i in group){
20942             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20943             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20944         }
20945         
20946     },
20947     
20948     clearInvalid : function()
20949     {
20950         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20951         
20952         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20953         
20954         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20955         
20956         if (label && label.iconEl) {
20957             label.iconEl.removeClass(label.validClass);
20958             label.iconEl.removeClass(label.invalidClass);
20959         }
20960     },
20961     
20962     disable : function()
20963     {
20964         if(this.inputType != 'radio'){
20965             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20966             return;
20967         }
20968         
20969         var _this = this;
20970         
20971         if(this.rendered){
20972             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20973                 _this.getActionEl().addClass(this.disabledClass);
20974                 e.dom.disabled = true;
20975             });
20976         }
20977         
20978         this.disabled = true;
20979         this.fireEvent("disable", this);
20980         return this;
20981     },
20982
20983     enable : function()
20984     {
20985         if(this.inputType != 'radio'){
20986             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20987             return;
20988         }
20989         
20990         var _this = this;
20991         
20992         if(this.rendered){
20993             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20994                 _this.getActionEl().removeClass(this.disabledClass);
20995                 e.dom.disabled = false;
20996             });
20997         }
20998         
20999         this.disabled = false;
21000         this.fireEvent("enable", this);
21001         return this;
21002     },
21003     
21004     setBoxLabel : function(v)
21005     {
21006         this.boxLabel = v;
21007         
21008         if(this.rendered){
21009             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21010         }
21011     }
21012
21013 });
21014
21015 Roo.apply(Roo.bootstrap.CheckBox, {
21016     
21017     groups: {},
21018     
21019      /**
21020     * register a CheckBox Group
21021     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21022     */
21023     register : function(checkbox)
21024     {
21025         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21026             this.groups[checkbox.groupId] = {};
21027         }
21028         
21029         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21030             return;
21031         }
21032         
21033         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21034         
21035     },
21036     /**
21037     * fetch a CheckBox Group based on the group ID
21038     * @param {string} the group ID
21039     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21040     */
21041     get: function(groupId) {
21042         if (typeof(this.groups[groupId]) == 'undefined') {
21043             return false;
21044         }
21045         
21046         return this.groups[groupId] ;
21047     }
21048     
21049     
21050 });
21051 /*
21052  * - LGPL
21053  *
21054  * RadioItem
21055  * 
21056  */
21057
21058 /**
21059  * @class Roo.bootstrap.Radio
21060  * @extends Roo.bootstrap.Component
21061  * Bootstrap Radio class
21062  * @cfg {String} boxLabel - the label associated
21063  * @cfg {String} value - the value of radio
21064  * 
21065  * @constructor
21066  * Create a new Radio
21067  * @param {Object} config The config object
21068  */
21069 Roo.bootstrap.Radio = function(config){
21070     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21071     
21072 };
21073
21074 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21075     
21076     boxLabel : '',
21077     
21078     value : '',
21079     
21080     getAutoCreate : function()
21081     {
21082         var cfg = {
21083             tag : 'div',
21084             cls : 'form-group radio',
21085             cn : [
21086                 {
21087                     tag : 'label',
21088                     cls : 'box-label',
21089                     html : this.boxLabel
21090                 }
21091             ]
21092         };
21093         
21094         return cfg;
21095     },
21096     
21097     initEvents : function() 
21098     {
21099         this.parent().register(this);
21100         
21101         this.el.on('click', this.onClick, this);
21102         
21103     },
21104     
21105     onClick : function(e)
21106     {
21107         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21108             this.setChecked(true);
21109         }
21110     },
21111     
21112     setChecked : function(state, suppressEvent)
21113     {
21114         this.parent().setValue(this.value, suppressEvent);
21115         
21116     },
21117     
21118     setBoxLabel : function(v)
21119     {
21120         this.boxLabel = v;
21121         
21122         if(this.rendered){
21123             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21124         }
21125     }
21126     
21127 });
21128  
21129
21130  /*
21131  * - LGPL
21132  *
21133  * Input
21134  * 
21135  */
21136
21137 /**
21138  * @class Roo.bootstrap.SecurePass
21139  * @extends Roo.bootstrap.Input
21140  * Bootstrap SecurePass class
21141  *
21142  * 
21143  * @constructor
21144  * Create a new SecurePass
21145  * @param {Object} config The config object
21146  */
21147  
21148 Roo.bootstrap.SecurePass = function (config) {
21149     // these go here, so the translation tool can replace them..
21150     this.errors = {
21151         PwdEmpty: "Please type a password, and then retype it to confirm.",
21152         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21153         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21154         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21155         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21156         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21157         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21158         TooWeak: "Your password is Too Weak."
21159     },
21160     this.meterLabel = "Password strength:";
21161     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21162     this.meterClass = [
21163         "roo-password-meter-tooweak", 
21164         "roo-password-meter-weak", 
21165         "roo-password-meter-medium", 
21166         "roo-password-meter-strong", 
21167         "roo-password-meter-grey"
21168     ];
21169     
21170     this.errors = {};
21171     
21172     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21173 }
21174
21175 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21176     /**
21177      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21178      * {
21179      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21180      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21181      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21182      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21183      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21184      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21185      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21186      * })
21187      */
21188     // private
21189     
21190     meterWidth: 300,
21191     errorMsg :'',    
21192     errors: false,
21193     imageRoot: '/',
21194     /**
21195      * @cfg {String/Object} Label for the strength meter (defaults to
21196      * 'Password strength:')
21197      */
21198     // private
21199     meterLabel: '',
21200     /**
21201      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21202      * ['Weak', 'Medium', 'Strong'])
21203      */
21204     // private    
21205     pwdStrengths: false,    
21206     // private
21207     strength: 0,
21208     // private
21209     _lastPwd: null,
21210     // private
21211     kCapitalLetter: 0,
21212     kSmallLetter: 1,
21213     kDigit: 2,
21214     kPunctuation: 3,
21215     
21216     insecure: false,
21217     // private
21218     initEvents: function ()
21219     {
21220         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21221
21222         if (this.el.is('input[type=password]') && Roo.isSafari) {
21223             this.el.on('keydown', this.SafariOnKeyDown, this);
21224         }
21225
21226         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21227     },
21228     // private
21229     onRender: function (ct, position)
21230     {
21231         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21232         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21233         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21234
21235         this.trigger.createChild({
21236                    cn: [
21237                     {
21238                     //id: 'PwdMeter',
21239                     tag: 'div',
21240                     cls: 'roo-password-meter-grey col-xs-12',
21241                     style: {
21242                         //width: 0,
21243                         //width: this.meterWidth + 'px'                                                
21244                         }
21245                     },
21246                     {                            
21247                          cls: 'roo-password-meter-text'                          
21248                     }
21249                 ]            
21250         });
21251
21252          
21253         if (this.hideTrigger) {
21254             this.trigger.setDisplayed(false);
21255         }
21256         this.setSize(this.width || '', this.height || '');
21257     },
21258     // private
21259     onDestroy: function ()
21260     {
21261         if (this.trigger) {
21262             this.trigger.removeAllListeners();
21263             this.trigger.remove();
21264         }
21265         if (this.wrap) {
21266             this.wrap.remove();
21267         }
21268         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21269     },
21270     // private
21271     checkStrength: function ()
21272     {
21273         var pwd = this.inputEl().getValue();
21274         if (pwd == this._lastPwd) {
21275             return;
21276         }
21277
21278         var strength;
21279         if (this.ClientSideStrongPassword(pwd)) {
21280             strength = 3;
21281         } else if (this.ClientSideMediumPassword(pwd)) {
21282             strength = 2;
21283         } else if (this.ClientSideWeakPassword(pwd)) {
21284             strength = 1;
21285         } else {
21286             strength = 0;
21287         }
21288         
21289         Roo.log('strength1: ' + strength);
21290         
21291         //var pm = this.trigger.child('div/div/div').dom;
21292         var pm = this.trigger.child('div/div');
21293         pm.removeClass(this.meterClass);
21294         pm.addClass(this.meterClass[strength]);
21295                 
21296         
21297         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21298                 
21299         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21300         
21301         this._lastPwd = pwd;
21302     },
21303     reset: function ()
21304     {
21305         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21306         
21307         this._lastPwd = '';
21308         
21309         var pm = this.trigger.child('div/div');
21310         pm.removeClass(this.meterClass);
21311         pm.addClass('roo-password-meter-grey');        
21312         
21313         
21314         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21315         
21316         pt.innerHTML = '';
21317         this.inputEl().dom.type='password';
21318     },
21319     // private
21320     validateValue: function (value)
21321     {
21322         
21323         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21324             return false;
21325         }
21326         if (value.length == 0) {
21327             if (this.allowBlank) {
21328                 this.clearInvalid();
21329                 return true;
21330             }
21331
21332             this.markInvalid(this.errors.PwdEmpty);
21333             this.errorMsg = this.errors.PwdEmpty;
21334             return false;
21335         }
21336         
21337         if(this.insecure){
21338             return true;
21339         }
21340         
21341         if ('[\x21-\x7e]*'.match(value)) {
21342             this.markInvalid(this.errors.PwdBadChar);
21343             this.errorMsg = this.errors.PwdBadChar;
21344             return false;
21345         }
21346         if (value.length < 6) {
21347             this.markInvalid(this.errors.PwdShort);
21348             this.errorMsg = this.errors.PwdShort;
21349             return false;
21350         }
21351         if (value.length > 16) {
21352             this.markInvalid(this.errors.PwdLong);
21353             this.errorMsg = this.errors.PwdLong;
21354             return false;
21355         }
21356         var strength;
21357         if (this.ClientSideStrongPassword(value)) {
21358             strength = 3;
21359         } else if (this.ClientSideMediumPassword(value)) {
21360             strength = 2;
21361         } else if (this.ClientSideWeakPassword(value)) {
21362             strength = 1;
21363         } else {
21364             strength = 0;
21365         }
21366
21367         
21368         if (strength < 2) {
21369             //this.markInvalid(this.errors.TooWeak);
21370             this.errorMsg = this.errors.TooWeak;
21371             //return false;
21372         }
21373         
21374         
21375         console.log('strength2: ' + strength);
21376         
21377         //var pm = this.trigger.child('div/div/div').dom;
21378         
21379         var pm = this.trigger.child('div/div');
21380         pm.removeClass(this.meterClass);
21381         pm.addClass(this.meterClass[strength]);
21382                 
21383         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21384                 
21385         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21386         
21387         this.errorMsg = ''; 
21388         return true;
21389     },
21390     // private
21391     CharacterSetChecks: function (type)
21392     {
21393         this.type = type;
21394         this.fResult = false;
21395     },
21396     // private
21397     isctype: function (character, type)
21398     {
21399         switch (type) {  
21400             case this.kCapitalLetter:
21401                 if (character >= 'A' && character <= 'Z') {
21402                     return true;
21403                 }
21404                 break;
21405             
21406             case this.kSmallLetter:
21407                 if (character >= 'a' && character <= 'z') {
21408                     return true;
21409                 }
21410                 break;
21411             
21412             case this.kDigit:
21413                 if (character >= '0' && character <= '9') {
21414                     return true;
21415                 }
21416                 break;
21417             
21418             case this.kPunctuation:
21419                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21420                     return true;
21421                 }
21422                 break;
21423             
21424             default:
21425                 return false;
21426         }
21427
21428     },
21429     // private
21430     IsLongEnough: function (pwd, size)
21431     {
21432         return !(pwd == null || isNaN(size) || pwd.length < size);
21433     },
21434     // private
21435     SpansEnoughCharacterSets: function (word, nb)
21436     {
21437         if (!this.IsLongEnough(word, nb))
21438         {
21439             return false;
21440         }
21441
21442         var characterSetChecks = new Array(
21443             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21444             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21445         );
21446         
21447         for (var index = 0; index < word.length; ++index) {
21448             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21449                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21450                     characterSetChecks[nCharSet].fResult = true;
21451                     break;
21452                 }
21453             }
21454         }
21455
21456         var nCharSets = 0;
21457         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21458             if (characterSetChecks[nCharSet].fResult) {
21459                 ++nCharSets;
21460             }
21461         }
21462
21463         if (nCharSets < nb) {
21464             return false;
21465         }
21466         return true;
21467     },
21468     // private
21469     ClientSideStrongPassword: function (pwd)
21470     {
21471         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21472     },
21473     // private
21474     ClientSideMediumPassword: function (pwd)
21475     {
21476         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21477     },
21478     // private
21479     ClientSideWeakPassword: function (pwd)
21480     {
21481         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21482     }
21483           
21484 })//<script type="text/javascript">
21485
21486 /*
21487  * Based  Ext JS Library 1.1.1
21488  * Copyright(c) 2006-2007, Ext JS, LLC.
21489  * LGPL
21490  *
21491  */
21492  
21493 /**
21494  * @class Roo.HtmlEditorCore
21495  * @extends Roo.Component
21496  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21497  *
21498  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21499  */
21500
21501 Roo.HtmlEditorCore = function(config){
21502     
21503     
21504     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21505     
21506     
21507     this.addEvents({
21508         /**
21509          * @event initialize
21510          * Fires when the editor is fully initialized (including the iframe)
21511          * @param {Roo.HtmlEditorCore} this
21512          */
21513         initialize: true,
21514         /**
21515          * @event activate
21516          * Fires when the editor is first receives the focus. Any insertion must wait
21517          * until after this event.
21518          * @param {Roo.HtmlEditorCore} this
21519          */
21520         activate: true,
21521          /**
21522          * @event beforesync
21523          * Fires before the textarea is updated with content from the editor iframe. Return false
21524          * to cancel the sync.
21525          * @param {Roo.HtmlEditorCore} this
21526          * @param {String} html
21527          */
21528         beforesync: true,
21529          /**
21530          * @event beforepush
21531          * Fires before the iframe editor is updated with content from the textarea. Return false
21532          * to cancel the push.
21533          * @param {Roo.HtmlEditorCore} this
21534          * @param {String} html
21535          */
21536         beforepush: true,
21537          /**
21538          * @event sync
21539          * Fires when the textarea is updated with content from the editor iframe.
21540          * @param {Roo.HtmlEditorCore} this
21541          * @param {String} html
21542          */
21543         sync: true,
21544          /**
21545          * @event push
21546          * Fires when the iframe editor is updated with content from the textarea.
21547          * @param {Roo.HtmlEditorCore} this
21548          * @param {String} html
21549          */
21550         push: true,
21551         
21552         /**
21553          * @event editorevent
21554          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21555          * @param {Roo.HtmlEditorCore} this
21556          */
21557         editorevent: true
21558         
21559     });
21560     
21561     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21562     
21563     // defaults : white / black...
21564     this.applyBlacklists();
21565     
21566     
21567     
21568 };
21569
21570
21571 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21572
21573
21574      /**
21575      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21576      */
21577     
21578     owner : false,
21579     
21580      /**
21581      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21582      *                        Roo.resizable.
21583      */
21584     resizable : false,
21585      /**
21586      * @cfg {Number} height (in pixels)
21587      */   
21588     height: 300,
21589    /**
21590      * @cfg {Number} width (in pixels)
21591      */   
21592     width: 500,
21593     
21594     /**
21595      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21596      * 
21597      */
21598     stylesheets: false,
21599     
21600     // id of frame..
21601     frameId: false,
21602     
21603     // private properties
21604     validationEvent : false,
21605     deferHeight: true,
21606     initialized : false,
21607     activated : false,
21608     sourceEditMode : false,
21609     onFocus : Roo.emptyFn,
21610     iframePad:3,
21611     hideMode:'offsets',
21612     
21613     clearUp: true,
21614     
21615     // blacklist + whitelisted elements..
21616     black: false,
21617     white: false,
21618      
21619     bodyCls : '',
21620
21621     /**
21622      * Protected method that will not generally be called directly. It
21623      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21624      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21625      */
21626     getDocMarkup : function(){
21627         // body styles..
21628         var st = '';
21629         
21630         // inherit styels from page...?? 
21631         if (this.stylesheets === false) {
21632             
21633             Roo.get(document.head).select('style').each(function(node) {
21634                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21635             });
21636             
21637             Roo.get(document.head).select('link').each(function(node) { 
21638                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21639             });
21640             
21641         } else if (!this.stylesheets.length) {
21642                 // simple..
21643                 st = '<style type="text/css">' +
21644                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21645                    '</style>';
21646         } else { 
21647             st = '<style type="text/css">' +
21648                     this.stylesheets +
21649                 '</style>';
21650         }
21651         
21652         st +=  '<style type="text/css">' +
21653             'IMG { cursor: pointer } ' +
21654         '</style>';
21655
21656         var cls = 'roo-htmleditor-body';
21657         
21658         if(this.bodyCls.length){
21659             cls += ' ' + this.bodyCls;
21660         }
21661         
21662         return '<html><head>' + st  +
21663             //<style type="text/css">' +
21664             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21665             //'</style>' +
21666             ' </head><body class="' +  cls + '"></body></html>';
21667     },
21668
21669     // private
21670     onRender : function(ct, position)
21671     {
21672         var _t = this;
21673         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21674         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21675         
21676         
21677         this.el.dom.style.border = '0 none';
21678         this.el.dom.setAttribute('tabIndex', -1);
21679         this.el.addClass('x-hidden hide');
21680         
21681         
21682         
21683         if(Roo.isIE){ // fix IE 1px bogus margin
21684             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21685         }
21686        
21687         
21688         this.frameId = Roo.id();
21689         
21690          
21691         
21692         var iframe = this.owner.wrap.createChild({
21693             tag: 'iframe',
21694             cls: 'form-control', // bootstrap..
21695             id: this.frameId,
21696             name: this.frameId,
21697             frameBorder : 'no',
21698             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21699         }, this.el
21700         );
21701         
21702         
21703         this.iframe = iframe.dom;
21704
21705          this.assignDocWin();
21706         
21707         this.doc.designMode = 'on';
21708        
21709         this.doc.open();
21710         this.doc.write(this.getDocMarkup());
21711         this.doc.close();
21712
21713         
21714         var task = { // must defer to wait for browser to be ready
21715             run : function(){
21716                 //console.log("run task?" + this.doc.readyState);
21717                 this.assignDocWin();
21718                 if(this.doc.body || this.doc.readyState == 'complete'){
21719                     try {
21720                         this.doc.designMode="on";
21721                     } catch (e) {
21722                         return;
21723                     }
21724                     Roo.TaskMgr.stop(task);
21725                     this.initEditor.defer(10, this);
21726                 }
21727             },
21728             interval : 10,
21729             duration: 10000,
21730             scope: this
21731         };
21732         Roo.TaskMgr.start(task);
21733
21734     },
21735
21736     // private
21737     onResize : function(w, h)
21738     {
21739          Roo.log('resize: ' +w + ',' + h );
21740         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21741         if(!this.iframe){
21742             return;
21743         }
21744         if(typeof w == 'number'){
21745             
21746             this.iframe.style.width = w + 'px';
21747         }
21748         if(typeof h == 'number'){
21749             
21750             this.iframe.style.height = h + 'px';
21751             if(this.doc){
21752                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21753             }
21754         }
21755         
21756     },
21757
21758     /**
21759      * Toggles the editor between standard and source edit mode.
21760      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21761      */
21762     toggleSourceEdit : function(sourceEditMode){
21763         
21764         this.sourceEditMode = sourceEditMode === true;
21765         
21766         if(this.sourceEditMode){
21767  
21768             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21769             
21770         }else{
21771             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21772             //this.iframe.className = '';
21773             this.deferFocus();
21774         }
21775         //this.setSize(this.owner.wrap.getSize());
21776         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21777     },
21778
21779     
21780   
21781
21782     /**
21783      * Protected method that will not generally be called directly. If you need/want
21784      * custom HTML cleanup, this is the method you should override.
21785      * @param {String} html The HTML to be cleaned
21786      * return {String} The cleaned HTML
21787      */
21788     cleanHtml : function(html){
21789         html = String(html);
21790         if(html.length > 5){
21791             if(Roo.isSafari){ // strip safari nonsense
21792                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21793             }
21794         }
21795         if(html == '&nbsp;'){
21796             html = '';
21797         }
21798         return html;
21799     },
21800
21801     /**
21802      * HTML Editor -> Textarea
21803      * Protected method that will not generally be called directly. Syncs the contents
21804      * of the editor iframe with the textarea.
21805      */
21806     syncValue : function(){
21807         if(this.initialized){
21808             var bd = (this.doc.body || this.doc.documentElement);
21809             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21810             var html = bd.innerHTML;
21811             if(Roo.isSafari){
21812                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21813                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21814                 if(m && m[1]){
21815                     html = '<div style="'+m[0]+'">' + html + '</div>';
21816                 }
21817             }
21818             html = this.cleanHtml(html);
21819             // fix up the special chars.. normaly like back quotes in word...
21820             // however we do not want to do this with chinese..
21821             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21822                 var cc = b.charCodeAt();
21823                 if (
21824                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21825                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21826                     (cc >= 0xf900 && cc < 0xfb00 )
21827                 ) {
21828                         return b;
21829                 }
21830                 return "&#"+cc+";" 
21831             });
21832             if(this.owner.fireEvent('beforesync', this, html) !== false){
21833                 this.el.dom.value = html;
21834                 this.owner.fireEvent('sync', this, html);
21835             }
21836         }
21837     },
21838
21839     /**
21840      * Protected method that will not generally be called directly. Pushes the value of the textarea
21841      * into the iframe editor.
21842      */
21843     pushValue : function(){
21844         if(this.initialized){
21845             var v = this.el.dom.value.trim();
21846             
21847 //            if(v.length < 1){
21848 //                v = '&#160;';
21849 //            }
21850             
21851             if(this.owner.fireEvent('beforepush', this, v) !== false){
21852                 var d = (this.doc.body || this.doc.documentElement);
21853                 d.innerHTML = v;
21854                 this.cleanUpPaste();
21855                 this.el.dom.value = d.innerHTML;
21856                 this.owner.fireEvent('push', this, v);
21857             }
21858         }
21859     },
21860
21861     // private
21862     deferFocus : function(){
21863         this.focus.defer(10, this);
21864     },
21865
21866     // doc'ed in Field
21867     focus : function(){
21868         if(this.win && !this.sourceEditMode){
21869             this.win.focus();
21870         }else{
21871             this.el.focus();
21872         }
21873     },
21874     
21875     assignDocWin: function()
21876     {
21877         var iframe = this.iframe;
21878         
21879          if(Roo.isIE){
21880             this.doc = iframe.contentWindow.document;
21881             this.win = iframe.contentWindow;
21882         } else {
21883 //            if (!Roo.get(this.frameId)) {
21884 //                return;
21885 //            }
21886 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21887 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21888             
21889             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21890                 return;
21891             }
21892             
21893             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21894             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21895         }
21896     },
21897     
21898     // private
21899     initEditor : function(){
21900         //console.log("INIT EDITOR");
21901         this.assignDocWin();
21902         
21903         
21904         
21905         this.doc.designMode="on";
21906         this.doc.open();
21907         this.doc.write(this.getDocMarkup());
21908         this.doc.close();
21909         
21910         var dbody = (this.doc.body || this.doc.documentElement);
21911         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21912         // this copies styles from the containing element into thsi one..
21913         // not sure why we need all of this..
21914         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21915         
21916         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21917         //ss['background-attachment'] = 'fixed'; // w3c
21918         dbody.bgProperties = 'fixed'; // ie
21919         //Roo.DomHelper.applyStyles(dbody, ss);
21920         Roo.EventManager.on(this.doc, {
21921             //'mousedown': this.onEditorEvent,
21922             'mouseup': this.onEditorEvent,
21923             'dblclick': this.onEditorEvent,
21924             'click': this.onEditorEvent,
21925             'keyup': this.onEditorEvent,
21926             buffer:100,
21927             scope: this
21928         });
21929         if(Roo.isGecko){
21930             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21931         }
21932         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21933             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21934         }
21935         this.initialized = true;
21936
21937         this.owner.fireEvent('initialize', this);
21938         this.pushValue();
21939     },
21940
21941     // private
21942     onDestroy : function(){
21943         
21944         
21945         
21946         if(this.rendered){
21947             
21948             //for (var i =0; i < this.toolbars.length;i++) {
21949             //    // fixme - ask toolbars for heights?
21950             //    this.toolbars[i].onDestroy();
21951            // }
21952             
21953             //this.wrap.dom.innerHTML = '';
21954             //this.wrap.remove();
21955         }
21956     },
21957
21958     // private
21959     onFirstFocus : function(){
21960         
21961         this.assignDocWin();
21962         
21963         
21964         this.activated = true;
21965          
21966     
21967         if(Roo.isGecko){ // prevent silly gecko errors
21968             this.win.focus();
21969             var s = this.win.getSelection();
21970             if(!s.focusNode || s.focusNode.nodeType != 3){
21971                 var r = s.getRangeAt(0);
21972                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21973                 r.collapse(true);
21974                 this.deferFocus();
21975             }
21976             try{
21977                 this.execCmd('useCSS', true);
21978                 this.execCmd('styleWithCSS', false);
21979             }catch(e){}
21980         }
21981         this.owner.fireEvent('activate', this);
21982     },
21983
21984     // private
21985     adjustFont: function(btn){
21986         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21987         //if(Roo.isSafari){ // safari
21988         //    adjust *= 2;
21989        // }
21990         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21991         if(Roo.isSafari){ // safari
21992             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21993             v =  (v < 10) ? 10 : v;
21994             v =  (v > 48) ? 48 : v;
21995             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21996             
21997         }
21998         
21999         
22000         v = Math.max(1, v+adjust);
22001         
22002         this.execCmd('FontSize', v  );
22003     },
22004
22005     onEditorEvent : function(e)
22006     {
22007         this.owner.fireEvent('editorevent', this, e);
22008       //  this.updateToolbar();
22009         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22010     },
22011
22012     insertTag : function(tg)
22013     {
22014         // could be a bit smarter... -> wrap the current selected tRoo..
22015         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22016             
22017             range = this.createRange(this.getSelection());
22018             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22019             wrappingNode.appendChild(range.extractContents());
22020             range.insertNode(wrappingNode);
22021
22022             return;
22023             
22024             
22025             
22026         }
22027         this.execCmd("formatblock",   tg);
22028         
22029     },
22030     
22031     insertText : function(txt)
22032     {
22033         
22034         
22035         var range = this.createRange();
22036         range.deleteContents();
22037                //alert(Sender.getAttribute('label'));
22038                
22039         range.insertNode(this.doc.createTextNode(txt));
22040     } ,
22041     
22042      
22043
22044     /**
22045      * Executes a Midas editor command on the editor document and performs necessary focus and
22046      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22047      * @param {String} cmd The Midas command
22048      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22049      */
22050     relayCmd : function(cmd, value){
22051         this.win.focus();
22052         this.execCmd(cmd, value);
22053         this.owner.fireEvent('editorevent', this);
22054         //this.updateToolbar();
22055         this.owner.deferFocus();
22056     },
22057
22058     /**
22059      * Executes a Midas editor command directly on the editor document.
22060      * For visual commands, you should use {@link #relayCmd} instead.
22061      * <b>This should only be called after the editor is initialized.</b>
22062      * @param {String} cmd The Midas command
22063      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22064      */
22065     execCmd : function(cmd, value){
22066         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22067         this.syncValue();
22068     },
22069  
22070  
22071    
22072     /**
22073      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22074      * to insert tRoo.
22075      * @param {String} text | dom node.. 
22076      */
22077     insertAtCursor : function(text)
22078     {
22079         
22080         if(!this.activated){
22081             return;
22082         }
22083         /*
22084         if(Roo.isIE){
22085             this.win.focus();
22086             var r = this.doc.selection.createRange();
22087             if(r){
22088                 r.collapse(true);
22089                 r.pasteHTML(text);
22090                 this.syncValue();
22091                 this.deferFocus();
22092             
22093             }
22094             return;
22095         }
22096         */
22097         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22098             this.win.focus();
22099             
22100             
22101             // from jquery ui (MIT licenced)
22102             var range, node;
22103             var win = this.win;
22104             
22105             if (win.getSelection && win.getSelection().getRangeAt) {
22106                 range = win.getSelection().getRangeAt(0);
22107                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22108                 range.insertNode(node);
22109             } else if (win.document.selection && win.document.selection.createRange) {
22110                 // no firefox support
22111                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22112                 win.document.selection.createRange().pasteHTML(txt);
22113             } else {
22114                 // no firefox support
22115                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22116                 this.execCmd('InsertHTML', txt);
22117             } 
22118             
22119             this.syncValue();
22120             
22121             this.deferFocus();
22122         }
22123     },
22124  // private
22125     mozKeyPress : function(e){
22126         if(e.ctrlKey){
22127             var c = e.getCharCode(), cmd;
22128           
22129             if(c > 0){
22130                 c = String.fromCharCode(c).toLowerCase();
22131                 switch(c){
22132                     case 'b':
22133                         cmd = 'bold';
22134                         break;
22135                     case 'i':
22136                         cmd = 'italic';
22137                         break;
22138                     
22139                     case 'u':
22140                         cmd = 'underline';
22141                         break;
22142                     
22143                     case 'v':
22144                         this.cleanUpPaste.defer(100, this);
22145                         return;
22146                         
22147                 }
22148                 if(cmd){
22149                     this.win.focus();
22150                     this.execCmd(cmd);
22151                     this.deferFocus();
22152                     e.preventDefault();
22153                 }
22154                 
22155             }
22156         }
22157     },
22158
22159     // private
22160     fixKeys : function(){ // load time branching for fastest keydown performance
22161         if(Roo.isIE){
22162             return function(e){
22163                 var k = e.getKey(), r;
22164                 if(k == e.TAB){
22165                     e.stopEvent();
22166                     r = this.doc.selection.createRange();
22167                     if(r){
22168                         r.collapse(true);
22169                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22170                         this.deferFocus();
22171                     }
22172                     return;
22173                 }
22174                 
22175                 if(k == e.ENTER){
22176                     r = this.doc.selection.createRange();
22177                     if(r){
22178                         var target = r.parentElement();
22179                         if(!target || target.tagName.toLowerCase() != 'li'){
22180                             e.stopEvent();
22181                             r.pasteHTML('<br />');
22182                             r.collapse(false);
22183                             r.select();
22184                         }
22185                     }
22186                 }
22187                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22188                     this.cleanUpPaste.defer(100, this);
22189                     return;
22190                 }
22191                 
22192                 
22193             };
22194         }else if(Roo.isOpera){
22195             return function(e){
22196                 var k = e.getKey();
22197                 if(k == e.TAB){
22198                     e.stopEvent();
22199                     this.win.focus();
22200                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22201                     this.deferFocus();
22202                 }
22203                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22204                     this.cleanUpPaste.defer(100, this);
22205                     return;
22206                 }
22207                 
22208             };
22209         }else if(Roo.isSafari){
22210             return function(e){
22211                 var k = e.getKey();
22212                 
22213                 if(k == e.TAB){
22214                     e.stopEvent();
22215                     this.execCmd('InsertText','\t');
22216                     this.deferFocus();
22217                     return;
22218                 }
22219                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22220                     this.cleanUpPaste.defer(100, this);
22221                     return;
22222                 }
22223                 
22224              };
22225         }
22226     }(),
22227     
22228     getAllAncestors: function()
22229     {
22230         var p = this.getSelectedNode();
22231         var a = [];
22232         if (!p) {
22233             a.push(p); // push blank onto stack..
22234             p = this.getParentElement();
22235         }
22236         
22237         
22238         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22239             a.push(p);
22240             p = p.parentNode;
22241         }
22242         a.push(this.doc.body);
22243         return a;
22244     },
22245     lastSel : false,
22246     lastSelNode : false,
22247     
22248     
22249     getSelection : function() 
22250     {
22251         this.assignDocWin();
22252         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22253     },
22254     
22255     getSelectedNode: function() 
22256     {
22257         // this may only work on Gecko!!!
22258         
22259         // should we cache this!!!!
22260         
22261         
22262         
22263          
22264         var range = this.createRange(this.getSelection()).cloneRange();
22265         
22266         if (Roo.isIE) {
22267             var parent = range.parentElement();
22268             while (true) {
22269                 var testRange = range.duplicate();
22270                 testRange.moveToElementText(parent);
22271                 if (testRange.inRange(range)) {
22272                     break;
22273                 }
22274                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22275                     break;
22276                 }
22277                 parent = parent.parentElement;
22278             }
22279             return parent;
22280         }
22281         
22282         // is ancestor a text element.
22283         var ac =  range.commonAncestorContainer;
22284         if (ac.nodeType == 3) {
22285             ac = ac.parentNode;
22286         }
22287         
22288         var ar = ac.childNodes;
22289          
22290         var nodes = [];
22291         var other_nodes = [];
22292         var has_other_nodes = false;
22293         for (var i=0;i<ar.length;i++) {
22294             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22295                 continue;
22296             }
22297             // fullly contained node.
22298             
22299             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22300                 nodes.push(ar[i]);
22301                 continue;
22302             }
22303             
22304             // probably selected..
22305             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22306                 other_nodes.push(ar[i]);
22307                 continue;
22308             }
22309             // outer..
22310             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22311                 continue;
22312             }
22313             
22314             
22315             has_other_nodes = true;
22316         }
22317         if (!nodes.length && other_nodes.length) {
22318             nodes= other_nodes;
22319         }
22320         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22321             return false;
22322         }
22323         
22324         return nodes[0];
22325     },
22326     createRange: function(sel)
22327     {
22328         // this has strange effects when using with 
22329         // top toolbar - not sure if it's a great idea.
22330         //this.editor.contentWindow.focus();
22331         if (typeof sel != "undefined") {
22332             try {
22333                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22334             } catch(e) {
22335                 return this.doc.createRange();
22336             }
22337         } else {
22338             return this.doc.createRange();
22339         }
22340     },
22341     getParentElement: function()
22342     {
22343         
22344         this.assignDocWin();
22345         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22346         
22347         var range = this.createRange(sel);
22348          
22349         try {
22350             var p = range.commonAncestorContainer;
22351             while (p.nodeType == 3) { // text node
22352                 p = p.parentNode;
22353             }
22354             return p;
22355         } catch (e) {
22356             return null;
22357         }
22358     
22359     },
22360     /***
22361      *
22362      * Range intersection.. the hard stuff...
22363      *  '-1' = before
22364      *  '0' = hits..
22365      *  '1' = after.
22366      *         [ -- selected range --- ]
22367      *   [fail]                        [fail]
22368      *
22369      *    basically..
22370      *      if end is before start or  hits it. fail.
22371      *      if start is after end or hits it fail.
22372      *
22373      *   if either hits (but other is outside. - then it's not 
22374      *   
22375      *    
22376      **/
22377     
22378     
22379     // @see http://www.thismuchiknow.co.uk/?p=64.
22380     rangeIntersectsNode : function(range, node)
22381     {
22382         var nodeRange = node.ownerDocument.createRange();
22383         try {
22384             nodeRange.selectNode(node);
22385         } catch (e) {
22386             nodeRange.selectNodeContents(node);
22387         }
22388     
22389         var rangeStartRange = range.cloneRange();
22390         rangeStartRange.collapse(true);
22391     
22392         var rangeEndRange = range.cloneRange();
22393         rangeEndRange.collapse(false);
22394     
22395         var nodeStartRange = nodeRange.cloneRange();
22396         nodeStartRange.collapse(true);
22397     
22398         var nodeEndRange = nodeRange.cloneRange();
22399         nodeEndRange.collapse(false);
22400     
22401         return rangeStartRange.compareBoundaryPoints(
22402                  Range.START_TO_START, nodeEndRange) == -1 &&
22403                rangeEndRange.compareBoundaryPoints(
22404                  Range.START_TO_START, nodeStartRange) == 1;
22405         
22406          
22407     },
22408     rangeCompareNode : function(range, node)
22409     {
22410         var nodeRange = node.ownerDocument.createRange();
22411         try {
22412             nodeRange.selectNode(node);
22413         } catch (e) {
22414             nodeRange.selectNodeContents(node);
22415         }
22416         
22417         
22418         range.collapse(true);
22419     
22420         nodeRange.collapse(true);
22421      
22422         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22423         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22424          
22425         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22426         
22427         var nodeIsBefore   =  ss == 1;
22428         var nodeIsAfter    = ee == -1;
22429         
22430         if (nodeIsBefore && nodeIsAfter) {
22431             return 0; // outer
22432         }
22433         if (!nodeIsBefore && nodeIsAfter) {
22434             return 1; //right trailed.
22435         }
22436         
22437         if (nodeIsBefore && !nodeIsAfter) {
22438             return 2;  // left trailed.
22439         }
22440         // fully contined.
22441         return 3;
22442     },
22443
22444     // private? - in a new class?
22445     cleanUpPaste :  function()
22446     {
22447         // cleans up the whole document..
22448         Roo.log('cleanuppaste');
22449         
22450         this.cleanUpChildren(this.doc.body);
22451         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22452         if (clean != this.doc.body.innerHTML) {
22453             this.doc.body.innerHTML = clean;
22454         }
22455         
22456     },
22457     
22458     cleanWordChars : function(input) {// change the chars to hex code
22459         var he = Roo.HtmlEditorCore;
22460         
22461         var output = input;
22462         Roo.each(he.swapCodes, function(sw) { 
22463             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22464             
22465             output = output.replace(swapper, sw[1]);
22466         });
22467         
22468         return output;
22469     },
22470     
22471     
22472     cleanUpChildren : function (n)
22473     {
22474         if (!n.childNodes.length) {
22475             return;
22476         }
22477         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22478            this.cleanUpChild(n.childNodes[i]);
22479         }
22480     },
22481     
22482     
22483         
22484     
22485     cleanUpChild : function (node)
22486     {
22487         var ed = this;
22488         //console.log(node);
22489         if (node.nodeName == "#text") {
22490             // clean up silly Windows -- stuff?
22491             return; 
22492         }
22493         if (node.nodeName == "#comment") {
22494             node.parentNode.removeChild(node);
22495             // clean up silly Windows -- stuff?
22496             return; 
22497         }
22498         var lcname = node.tagName.toLowerCase();
22499         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22500         // whitelist of tags..
22501         
22502         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22503             // remove node.
22504             node.parentNode.removeChild(node);
22505             return;
22506             
22507         }
22508         
22509         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22510         
22511         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22512         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22513         
22514         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22515         //    remove_keep_children = true;
22516         //}
22517         
22518         if (remove_keep_children) {
22519             this.cleanUpChildren(node);
22520             // inserts everything just before this node...
22521             while (node.childNodes.length) {
22522                 var cn = node.childNodes[0];
22523                 node.removeChild(cn);
22524                 node.parentNode.insertBefore(cn, node);
22525             }
22526             node.parentNode.removeChild(node);
22527             return;
22528         }
22529         
22530         if (!node.attributes || !node.attributes.length) {
22531             this.cleanUpChildren(node);
22532             return;
22533         }
22534         
22535         function cleanAttr(n,v)
22536         {
22537             
22538             if (v.match(/^\./) || v.match(/^\//)) {
22539                 return;
22540             }
22541             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22542                 return;
22543             }
22544             if (v.match(/^#/)) {
22545                 return;
22546             }
22547 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22548             node.removeAttribute(n);
22549             
22550         }
22551         
22552         var cwhite = this.cwhite;
22553         var cblack = this.cblack;
22554             
22555         function cleanStyle(n,v)
22556         {
22557             if (v.match(/expression/)) { //XSS?? should we even bother..
22558                 node.removeAttribute(n);
22559                 return;
22560             }
22561             
22562             var parts = v.split(/;/);
22563             var clean = [];
22564             
22565             Roo.each(parts, function(p) {
22566                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22567                 if (!p.length) {
22568                     return true;
22569                 }
22570                 var l = p.split(':').shift().replace(/\s+/g,'');
22571                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22572                 
22573                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22574 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22575                     //node.removeAttribute(n);
22576                     return true;
22577                 }
22578                 //Roo.log()
22579                 // only allow 'c whitelisted system attributes'
22580                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22581 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22582                     //node.removeAttribute(n);
22583                     return true;
22584                 }
22585                 
22586                 
22587                  
22588                 
22589                 clean.push(p);
22590                 return true;
22591             });
22592             if (clean.length) { 
22593                 node.setAttribute(n, clean.join(';'));
22594             } else {
22595                 node.removeAttribute(n);
22596             }
22597             
22598         }
22599         
22600         
22601         for (var i = node.attributes.length-1; i > -1 ; i--) {
22602             var a = node.attributes[i];
22603             //console.log(a);
22604             
22605             if (a.name.toLowerCase().substr(0,2)=='on')  {
22606                 node.removeAttribute(a.name);
22607                 continue;
22608             }
22609             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22610                 node.removeAttribute(a.name);
22611                 continue;
22612             }
22613             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22614                 cleanAttr(a.name,a.value); // fixme..
22615                 continue;
22616             }
22617             if (a.name == 'style') {
22618                 cleanStyle(a.name,a.value);
22619                 continue;
22620             }
22621             /// clean up MS crap..
22622             // tecnically this should be a list of valid class'es..
22623             
22624             
22625             if (a.name == 'class') {
22626                 if (a.value.match(/^Mso/)) {
22627                     node.className = '';
22628                 }
22629                 
22630                 if (a.value.match(/^body$/)) {
22631                     node.className = '';
22632                 }
22633                 continue;
22634             }
22635             
22636             // style cleanup!?
22637             // class cleanup?
22638             
22639         }
22640         
22641         
22642         this.cleanUpChildren(node);
22643         
22644         
22645     },
22646     
22647     /**
22648      * Clean up MS wordisms...
22649      */
22650     cleanWord : function(node)
22651     {
22652         
22653         
22654         if (!node) {
22655             this.cleanWord(this.doc.body);
22656             return;
22657         }
22658         if (node.nodeName == "#text") {
22659             // clean up silly Windows -- stuff?
22660             return; 
22661         }
22662         if (node.nodeName == "#comment") {
22663             node.parentNode.removeChild(node);
22664             // clean up silly Windows -- stuff?
22665             return; 
22666         }
22667         
22668         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22669             node.parentNode.removeChild(node);
22670             return;
22671         }
22672         
22673         // remove - but keep children..
22674         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22675             while (node.childNodes.length) {
22676                 var cn = node.childNodes[0];
22677                 node.removeChild(cn);
22678                 node.parentNode.insertBefore(cn, node);
22679             }
22680             node.parentNode.removeChild(node);
22681             this.iterateChildren(node, this.cleanWord);
22682             return;
22683         }
22684         // clean styles
22685         if (node.className.length) {
22686             
22687             var cn = node.className.split(/\W+/);
22688             var cna = [];
22689             Roo.each(cn, function(cls) {
22690                 if (cls.match(/Mso[a-zA-Z]+/)) {
22691                     return;
22692                 }
22693                 cna.push(cls);
22694             });
22695             node.className = cna.length ? cna.join(' ') : '';
22696             if (!cna.length) {
22697                 node.removeAttribute("class");
22698             }
22699         }
22700         
22701         if (node.hasAttribute("lang")) {
22702             node.removeAttribute("lang");
22703         }
22704         
22705         if (node.hasAttribute("style")) {
22706             
22707             var styles = node.getAttribute("style").split(";");
22708             var nstyle = [];
22709             Roo.each(styles, function(s) {
22710                 if (!s.match(/:/)) {
22711                     return;
22712                 }
22713                 var kv = s.split(":");
22714                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22715                     return;
22716                 }
22717                 // what ever is left... we allow.
22718                 nstyle.push(s);
22719             });
22720             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22721             if (!nstyle.length) {
22722                 node.removeAttribute('style');
22723             }
22724         }
22725         this.iterateChildren(node, this.cleanWord);
22726         
22727         
22728         
22729     },
22730     /**
22731      * iterateChildren of a Node, calling fn each time, using this as the scole..
22732      * @param {DomNode} node node to iterate children of.
22733      * @param {Function} fn method of this class to call on each item.
22734      */
22735     iterateChildren : function(node, fn)
22736     {
22737         if (!node.childNodes.length) {
22738                 return;
22739         }
22740         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22741            fn.call(this, node.childNodes[i])
22742         }
22743     },
22744     
22745     
22746     /**
22747      * cleanTableWidths.
22748      *
22749      * Quite often pasting from word etc.. results in tables with column and widths.
22750      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22751      *
22752      */
22753     cleanTableWidths : function(node)
22754     {
22755          
22756          
22757         if (!node) {
22758             this.cleanTableWidths(this.doc.body);
22759             return;
22760         }
22761         
22762         // ignore list...
22763         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22764             return; 
22765         }
22766         Roo.log(node.tagName);
22767         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22768             this.iterateChildren(node, this.cleanTableWidths);
22769             return;
22770         }
22771         if (node.hasAttribute('width')) {
22772             node.removeAttribute('width');
22773         }
22774         
22775          
22776         if (node.hasAttribute("style")) {
22777             // pretty basic...
22778             
22779             var styles = node.getAttribute("style").split(";");
22780             var nstyle = [];
22781             Roo.each(styles, function(s) {
22782                 if (!s.match(/:/)) {
22783                     return;
22784                 }
22785                 var kv = s.split(":");
22786                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22787                     return;
22788                 }
22789                 // what ever is left... we allow.
22790                 nstyle.push(s);
22791             });
22792             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22793             if (!nstyle.length) {
22794                 node.removeAttribute('style');
22795             }
22796         }
22797         
22798         this.iterateChildren(node, this.cleanTableWidths);
22799         
22800         
22801     },
22802     
22803     
22804     
22805     
22806     domToHTML : function(currentElement, depth, nopadtext) {
22807         
22808         depth = depth || 0;
22809         nopadtext = nopadtext || false;
22810     
22811         if (!currentElement) {
22812             return this.domToHTML(this.doc.body);
22813         }
22814         
22815         //Roo.log(currentElement);
22816         var j;
22817         var allText = false;
22818         var nodeName = currentElement.nodeName;
22819         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22820         
22821         if  (nodeName == '#text') {
22822             
22823             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22824         }
22825         
22826         
22827         var ret = '';
22828         if (nodeName != 'BODY') {
22829              
22830             var i = 0;
22831             // Prints the node tagName, such as <A>, <IMG>, etc
22832             if (tagName) {
22833                 var attr = [];
22834                 for(i = 0; i < currentElement.attributes.length;i++) {
22835                     // quoting?
22836                     var aname = currentElement.attributes.item(i).name;
22837                     if (!currentElement.attributes.item(i).value.length) {
22838                         continue;
22839                     }
22840                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22841                 }
22842                 
22843                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22844             } 
22845             else {
22846                 
22847                 // eack
22848             }
22849         } else {
22850             tagName = false;
22851         }
22852         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22853             return ret;
22854         }
22855         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22856             nopadtext = true;
22857         }
22858         
22859         
22860         // Traverse the tree
22861         i = 0;
22862         var currentElementChild = currentElement.childNodes.item(i);
22863         var allText = true;
22864         var innerHTML  = '';
22865         lastnode = '';
22866         while (currentElementChild) {
22867             // Formatting code (indent the tree so it looks nice on the screen)
22868             var nopad = nopadtext;
22869             if (lastnode == 'SPAN') {
22870                 nopad  = true;
22871             }
22872             // text
22873             if  (currentElementChild.nodeName == '#text') {
22874                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22875                 toadd = nopadtext ? toadd : toadd.trim();
22876                 if (!nopad && toadd.length > 80) {
22877                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22878                 }
22879                 innerHTML  += toadd;
22880                 
22881                 i++;
22882                 currentElementChild = currentElement.childNodes.item(i);
22883                 lastNode = '';
22884                 continue;
22885             }
22886             allText = false;
22887             
22888             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22889                 
22890             // Recursively traverse the tree structure of the child node
22891             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22892             lastnode = currentElementChild.nodeName;
22893             i++;
22894             currentElementChild=currentElement.childNodes.item(i);
22895         }
22896         
22897         ret += innerHTML;
22898         
22899         if (!allText) {
22900                 // The remaining code is mostly for formatting the tree
22901             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22902         }
22903         
22904         
22905         if (tagName) {
22906             ret+= "</"+tagName+">";
22907         }
22908         return ret;
22909         
22910     },
22911         
22912     applyBlacklists : function()
22913     {
22914         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22915         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22916         
22917         this.white = [];
22918         this.black = [];
22919         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22920             if (b.indexOf(tag) > -1) {
22921                 return;
22922             }
22923             this.white.push(tag);
22924             
22925         }, this);
22926         
22927         Roo.each(w, function(tag) {
22928             if (b.indexOf(tag) > -1) {
22929                 return;
22930             }
22931             if (this.white.indexOf(tag) > -1) {
22932                 return;
22933             }
22934             this.white.push(tag);
22935             
22936         }, this);
22937         
22938         
22939         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22940             if (w.indexOf(tag) > -1) {
22941                 return;
22942             }
22943             this.black.push(tag);
22944             
22945         }, this);
22946         
22947         Roo.each(b, function(tag) {
22948             if (w.indexOf(tag) > -1) {
22949                 return;
22950             }
22951             if (this.black.indexOf(tag) > -1) {
22952                 return;
22953             }
22954             this.black.push(tag);
22955             
22956         }, this);
22957         
22958         
22959         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22960         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22961         
22962         this.cwhite = [];
22963         this.cblack = [];
22964         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22965             if (b.indexOf(tag) > -1) {
22966                 return;
22967             }
22968             this.cwhite.push(tag);
22969             
22970         }, this);
22971         
22972         Roo.each(w, function(tag) {
22973             if (b.indexOf(tag) > -1) {
22974                 return;
22975             }
22976             if (this.cwhite.indexOf(tag) > -1) {
22977                 return;
22978             }
22979             this.cwhite.push(tag);
22980             
22981         }, this);
22982         
22983         
22984         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22985             if (w.indexOf(tag) > -1) {
22986                 return;
22987             }
22988             this.cblack.push(tag);
22989             
22990         }, this);
22991         
22992         Roo.each(b, function(tag) {
22993             if (w.indexOf(tag) > -1) {
22994                 return;
22995             }
22996             if (this.cblack.indexOf(tag) > -1) {
22997                 return;
22998             }
22999             this.cblack.push(tag);
23000             
23001         }, this);
23002     },
23003     
23004     setStylesheets : function(stylesheets)
23005     {
23006         if(typeof(stylesheets) == 'string'){
23007             Roo.get(this.iframe.contentDocument.head).createChild({
23008                 tag : 'link',
23009                 rel : 'stylesheet',
23010                 type : 'text/css',
23011                 href : stylesheets
23012             });
23013             
23014             return;
23015         }
23016         var _this = this;
23017      
23018         Roo.each(stylesheets, function(s) {
23019             if(!s.length){
23020                 return;
23021             }
23022             
23023             Roo.get(_this.iframe.contentDocument.head).createChild({
23024                 tag : 'link',
23025                 rel : 'stylesheet',
23026                 type : 'text/css',
23027                 href : s
23028             });
23029         });
23030
23031         
23032     },
23033     
23034     removeStylesheets : function()
23035     {
23036         var _this = this;
23037         
23038         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23039             s.remove();
23040         });
23041     },
23042     
23043     setStyle : function(style)
23044     {
23045         Roo.get(this.iframe.contentDocument.head).createChild({
23046             tag : 'style',
23047             type : 'text/css',
23048             html : style
23049         });
23050
23051         return;
23052     }
23053     
23054     // hide stuff that is not compatible
23055     /**
23056      * @event blur
23057      * @hide
23058      */
23059     /**
23060      * @event change
23061      * @hide
23062      */
23063     /**
23064      * @event focus
23065      * @hide
23066      */
23067     /**
23068      * @event specialkey
23069      * @hide
23070      */
23071     /**
23072      * @cfg {String} fieldClass @hide
23073      */
23074     /**
23075      * @cfg {String} focusClass @hide
23076      */
23077     /**
23078      * @cfg {String} autoCreate @hide
23079      */
23080     /**
23081      * @cfg {String} inputType @hide
23082      */
23083     /**
23084      * @cfg {String} invalidClass @hide
23085      */
23086     /**
23087      * @cfg {String} invalidText @hide
23088      */
23089     /**
23090      * @cfg {String} msgFx @hide
23091      */
23092     /**
23093      * @cfg {String} validateOnBlur @hide
23094      */
23095 });
23096
23097 Roo.HtmlEditorCore.white = [
23098         'area', 'br', 'img', 'input', 'hr', 'wbr',
23099         
23100        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23101        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23102        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23103        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23104        'table',   'ul',         'xmp', 
23105        
23106        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23107       'thead',   'tr', 
23108      
23109       'dir', 'menu', 'ol', 'ul', 'dl',
23110        
23111       'embed',  'object'
23112 ];
23113
23114
23115 Roo.HtmlEditorCore.black = [
23116     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23117         'applet', // 
23118         'base',   'basefont', 'bgsound', 'blink',  'body', 
23119         'frame',  'frameset', 'head',    'html',   'ilayer', 
23120         'iframe', 'layer',  'link',     'meta',    'object',   
23121         'script', 'style' ,'title',  'xml' // clean later..
23122 ];
23123 Roo.HtmlEditorCore.clean = [
23124     'script', 'style', 'title', 'xml'
23125 ];
23126 Roo.HtmlEditorCore.remove = [
23127     'font'
23128 ];
23129 // attributes..
23130
23131 Roo.HtmlEditorCore.ablack = [
23132     'on'
23133 ];
23134     
23135 Roo.HtmlEditorCore.aclean = [ 
23136     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23137 ];
23138
23139 // protocols..
23140 Roo.HtmlEditorCore.pwhite= [
23141         'http',  'https',  'mailto'
23142 ];
23143
23144 // white listed style attributes.
23145 Roo.HtmlEditorCore.cwhite= [
23146       //  'text-align', /// default is to allow most things..
23147       
23148          
23149 //        'font-size'//??
23150 ];
23151
23152 // black listed style attributes.
23153 Roo.HtmlEditorCore.cblack= [
23154       //  'font-size' -- this can be set by the project 
23155 ];
23156
23157
23158 Roo.HtmlEditorCore.swapCodes   =[ 
23159     [    8211, "--" ], 
23160     [    8212, "--" ], 
23161     [    8216,  "'" ],  
23162     [    8217, "'" ],  
23163     [    8220, '"' ],  
23164     [    8221, '"' ],  
23165     [    8226, "*" ],  
23166     [    8230, "..." ]
23167 ]; 
23168
23169     /*
23170  * - LGPL
23171  *
23172  * HtmlEditor
23173  * 
23174  */
23175
23176 /**
23177  * @class Roo.bootstrap.HtmlEditor
23178  * @extends Roo.bootstrap.TextArea
23179  * Bootstrap HtmlEditor class
23180
23181  * @constructor
23182  * Create a new HtmlEditor
23183  * @param {Object} config The config object
23184  */
23185
23186 Roo.bootstrap.HtmlEditor = function(config){
23187     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23188     if (!this.toolbars) {
23189         this.toolbars = [];
23190     }
23191     
23192     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23193     this.addEvents({
23194             /**
23195              * @event initialize
23196              * Fires when the editor is fully initialized (including the iframe)
23197              * @param {HtmlEditor} this
23198              */
23199             initialize: true,
23200             /**
23201              * @event activate
23202              * Fires when the editor is first receives the focus. Any insertion must wait
23203              * until after this event.
23204              * @param {HtmlEditor} this
23205              */
23206             activate: true,
23207              /**
23208              * @event beforesync
23209              * Fires before the textarea is updated with content from the editor iframe. Return false
23210              * to cancel the sync.
23211              * @param {HtmlEditor} this
23212              * @param {String} html
23213              */
23214             beforesync: true,
23215              /**
23216              * @event beforepush
23217              * Fires before the iframe editor is updated with content from the textarea. Return false
23218              * to cancel the push.
23219              * @param {HtmlEditor} this
23220              * @param {String} html
23221              */
23222             beforepush: true,
23223              /**
23224              * @event sync
23225              * Fires when the textarea is updated with content from the editor iframe.
23226              * @param {HtmlEditor} this
23227              * @param {String} html
23228              */
23229             sync: true,
23230              /**
23231              * @event push
23232              * Fires when the iframe editor is updated with content from the textarea.
23233              * @param {HtmlEditor} this
23234              * @param {String} html
23235              */
23236             push: true,
23237              /**
23238              * @event editmodechange
23239              * Fires when the editor switches edit modes
23240              * @param {HtmlEditor} this
23241              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23242              */
23243             editmodechange: true,
23244             /**
23245              * @event editorevent
23246              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23247              * @param {HtmlEditor} this
23248              */
23249             editorevent: true,
23250             /**
23251              * @event firstfocus
23252              * Fires when on first focus - needed by toolbars..
23253              * @param {HtmlEditor} this
23254              */
23255             firstfocus: true,
23256             /**
23257              * @event autosave
23258              * Auto save the htmlEditor value as a file into Events
23259              * @param {HtmlEditor} this
23260              */
23261             autosave: true,
23262             /**
23263              * @event savedpreview
23264              * preview the saved version of htmlEditor
23265              * @param {HtmlEditor} this
23266              */
23267             savedpreview: true
23268         });
23269 };
23270
23271
23272 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23273     
23274     
23275       /**
23276      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23277      */
23278     toolbars : false,
23279     
23280      /**
23281     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23282     */
23283     btns : [],
23284    
23285      /**
23286      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23287      *                        Roo.resizable.
23288      */
23289     resizable : false,
23290      /**
23291      * @cfg {Number} height (in pixels)
23292      */   
23293     height: 300,
23294    /**
23295      * @cfg {Number} width (in pixels)
23296      */   
23297     width: false,
23298     
23299     /**
23300      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23301      * 
23302      */
23303     stylesheets: false,
23304     
23305     // id of frame..
23306     frameId: false,
23307     
23308     // private properties
23309     validationEvent : false,
23310     deferHeight: true,
23311     initialized : false,
23312     activated : false,
23313     
23314     onFocus : Roo.emptyFn,
23315     iframePad:3,
23316     hideMode:'offsets',
23317     
23318     tbContainer : false,
23319     
23320     bodyCls : '',
23321     
23322     toolbarContainer :function() {
23323         return this.wrap.select('.x-html-editor-tb',true).first();
23324     },
23325
23326     /**
23327      * Protected method that will not generally be called directly. It
23328      * is called when the editor creates its toolbar. Override this method if you need to
23329      * add custom toolbar buttons.
23330      * @param {HtmlEditor} editor
23331      */
23332     createToolbar : function(){
23333         Roo.log('renewing');
23334         Roo.log("create toolbars");
23335         
23336         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23337         this.toolbars[0].render(this.toolbarContainer());
23338         
23339         return;
23340         
23341 //        if (!editor.toolbars || !editor.toolbars.length) {
23342 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23343 //        }
23344 //        
23345 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23346 //            editor.toolbars[i] = Roo.factory(
23347 //                    typeof(editor.toolbars[i]) == 'string' ?
23348 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23349 //                Roo.bootstrap.HtmlEditor);
23350 //            editor.toolbars[i].init(editor);
23351 //        }
23352     },
23353
23354      
23355     // private
23356     onRender : function(ct, position)
23357     {
23358        // Roo.log("Call onRender: " + this.xtype);
23359         var _t = this;
23360         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23361       
23362         this.wrap = this.inputEl().wrap({
23363             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23364         });
23365         
23366         this.editorcore.onRender(ct, position);
23367          
23368         if (this.resizable) {
23369             this.resizeEl = new Roo.Resizable(this.wrap, {
23370                 pinned : true,
23371                 wrap: true,
23372                 dynamic : true,
23373                 minHeight : this.height,
23374                 height: this.height,
23375                 handles : this.resizable,
23376                 width: this.width,
23377                 listeners : {
23378                     resize : function(r, w, h) {
23379                         _t.onResize(w,h); // -something
23380                     }
23381                 }
23382             });
23383             
23384         }
23385         this.createToolbar(this);
23386        
23387         
23388         if(!this.width && this.resizable){
23389             this.setSize(this.wrap.getSize());
23390         }
23391         if (this.resizeEl) {
23392             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23393             // should trigger onReize..
23394         }
23395         
23396     },
23397
23398     // private
23399     onResize : function(w, h)
23400     {
23401         Roo.log('resize: ' +w + ',' + h );
23402         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23403         var ew = false;
23404         var eh = false;
23405         
23406         if(this.inputEl() ){
23407             if(typeof w == 'number'){
23408                 var aw = w - this.wrap.getFrameWidth('lr');
23409                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23410                 ew = aw;
23411             }
23412             if(typeof h == 'number'){
23413                  var tbh = -11;  // fixme it needs to tool bar size!
23414                 for (var i =0; i < this.toolbars.length;i++) {
23415                     // fixme - ask toolbars for heights?
23416                     tbh += this.toolbars[i].el.getHeight();
23417                     //if (this.toolbars[i].footer) {
23418                     //    tbh += this.toolbars[i].footer.el.getHeight();
23419                     //}
23420                 }
23421               
23422                 
23423                 
23424                 
23425                 
23426                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23427                 ah -= 5; // knock a few pixes off for look..
23428                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23429                 var eh = ah;
23430             }
23431         }
23432         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23433         this.editorcore.onResize(ew,eh);
23434         
23435     },
23436
23437     /**
23438      * Toggles the editor between standard and source edit mode.
23439      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23440      */
23441     toggleSourceEdit : function(sourceEditMode)
23442     {
23443         this.editorcore.toggleSourceEdit(sourceEditMode);
23444         
23445         if(this.editorcore.sourceEditMode){
23446             Roo.log('editor - showing textarea');
23447             
23448 //            Roo.log('in');
23449 //            Roo.log(this.syncValue());
23450             this.syncValue();
23451             this.inputEl().removeClass(['hide', 'x-hidden']);
23452             this.inputEl().dom.removeAttribute('tabIndex');
23453             this.inputEl().focus();
23454         }else{
23455             Roo.log('editor - hiding textarea');
23456 //            Roo.log('out')
23457 //            Roo.log(this.pushValue()); 
23458             this.pushValue();
23459             
23460             this.inputEl().addClass(['hide', 'x-hidden']);
23461             this.inputEl().dom.setAttribute('tabIndex', -1);
23462             //this.deferFocus();
23463         }
23464          
23465         if(this.resizable){
23466             this.setSize(this.wrap.getSize());
23467         }
23468         
23469         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23470     },
23471  
23472     // private (for BoxComponent)
23473     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23474
23475     // private (for BoxComponent)
23476     getResizeEl : function(){
23477         return this.wrap;
23478     },
23479
23480     // private (for BoxComponent)
23481     getPositionEl : function(){
23482         return this.wrap;
23483     },
23484
23485     // private
23486     initEvents : function(){
23487         this.originalValue = this.getValue();
23488     },
23489
23490 //    /**
23491 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23492 //     * @method
23493 //     */
23494 //    markInvalid : Roo.emptyFn,
23495 //    /**
23496 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23497 //     * @method
23498 //     */
23499 //    clearInvalid : Roo.emptyFn,
23500
23501     setValue : function(v){
23502         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23503         this.editorcore.pushValue();
23504     },
23505
23506      
23507     // private
23508     deferFocus : function(){
23509         this.focus.defer(10, this);
23510     },
23511
23512     // doc'ed in Field
23513     focus : function(){
23514         this.editorcore.focus();
23515         
23516     },
23517       
23518
23519     // private
23520     onDestroy : function(){
23521         
23522         
23523         
23524         if(this.rendered){
23525             
23526             for (var i =0; i < this.toolbars.length;i++) {
23527                 // fixme - ask toolbars for heights?
23528                 this.toolbars[i].onDestroy();
23529             }
23530             
23531             this.wrap.dom.innerHTML = '';
23532             this.wrap.remove();
23533         }
23534     },
23535
23536     // private
23537     onFirstFocus : function(){
23538         //Roo.log("onFirstFocus");
23539         this.editorcore.onFirstFocus();
23540          for (var i =0; i < this.toolbars.length;i++) {
23541             this.toolbars[i].onFirstFocus();
23542         }
23543         
23544     },
23545     
23546     // private
23547     syncValue : function()
23548     {   
23549         this.editorcore.syncValue();
23550     },
23551     
23552     pushValue : function()
23553     {   
23554         this.editorcore.pushValue();
23555     }
23556      
23557     
23558     // hide stuff that is not compatible
23559     /**
23560      * @event blur
23561      * @hide
23562      */
23563     /**
23564      * @event change
23565      * @hide
23566      */
23567     /**
23568      * @event focus
23569      * @hide
23570      */
23571     /**
23572      * @event specialkey
23573      * @hide
23574      */
23575     /**
23576      * @cfg {String} fieldClass @hide
23577      */
23578     /**
23579      * @cfg {String} focusClass @hide
23580      */
23581     /**
23582      * @cfg {String} autoCreate @hide
23583      */
23584     /**
23585      * @cfg {String} inputType @hide
23586      */
23587     /**
23588      * @cfg {String} invalidClass @hide
23589      */
23590     /**
23591      * @cfg {String} invalidText @hide
23592      */
23593     /**
23594      * @cfg {String} msgFx @hide
23595      */
23596     /**
23597      * @cfg {String} validateOnBlur @hide
23598      */
23599 });
23600  
23601     
23602    
23603    
23604    
23605       
23606 Roo.namespace('Roo.bootstrap.htmleditor');
23607 /**
23608  * @class Roo.bootstrap.HtmlEditorToolbar1
23609  * Basic Toolbar
23610  * 
23611  * Usage:
23612  *
23613  new Roo.bootstrap.HtmlEditor({
23614     ....
23615     toolbars : [
23616         new Roo.bootstrap.HtmlEditorToolbar1({
23617             disable : { fonts: 1 , format: 1, ..., ... , ...],
23618             btns : [ .... ]
23619         })
23620     }
23621      
23622  * 
23623  * @cfg {Object} disable List of elements to disable..
23624  * @cfg {Array} btns List of additional buttons.
23625  * 
23626  * 
23627  * NEEDS Extra CSS? 
23628  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23629  */
23630  
23631 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23632 {
23633     
23634     Roo.apply(this, config);
23635     
23636     // default disabled, based on 'good practice'..
23637     this.disable = this.disable || {};
23638     Roo.applyIf(this.disable, {
23639         fontSize : true,
23640         colors : true,
23641         specialElements : true
23642     });
23643     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23644     
23645     this.editor = config.editor;
23646     this.editorcore = config.editor.editorcore;
23647     
23648     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23649     
23650     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23651     // dont call parent... till later.
23652 }
23653 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23654      
23655     bar : true,
23656     
23657     editor : false,
23658     editorcore : false,
23659     
23660     
23661     formats : [
23662         "p" ,  
23663         "h1","h2","h3","h4","h5","h6", 
23664         "pre", "code", 
23665         "abbr", "acronym", "address", "cite", "samp", "var",
23666         'div','span'
23667     ],
23668     
23669     onRender : function(ct, position)
23670     {
23671        // Roo.log("Call onRender: " + this.xtype);
23672         
23673        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23674        Roo.log(this.el);
23675        this.el.dom.style.marginBottom = '0';
23676        var _this = this;
23677        var editorcore = this.editorcore;
23678        var editor= this.editor;
23679        
23680        var children = [];
23681        var btn = function(id,cmd , toggle, handler, html){
23682        
23683             var  event = toggle ? 'toggle' : 'click';
23684        
23685             var a = {
23686                 size : 'sm',
23687                 xtype: 'Button',
23688                 xns: Roo.bootstrap,
23689                 glyphicon : id,
23690                 cmd : id || cmd,
23691                 enableToggle:toggle !== false,
23692                 html : html || '',
23693                 pressed : toggle ? false : null,
23694                 listeners : {}
23695             };
23696             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23697                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23698             };
23699             children.push(a);
23700             return a;
23701        }
23702        
23703     //    var cb_box = function...
23704         
23705         var style = {
23706                 xtype: 'Button',
23707                 size : 'sm',
23708                 xns: Roo.bootstrap,
23709                 glyphicon : 'font',
23710                 //html : 'submit'
23711                 menu : {
23712                     xtype: 'Menu',
23713                     xns: Roo.bootstrap,
23714                     items:  []
23715                 }
23716         };
23717         Roo.each(this.formats, function(f) {
23718             style.menu.items.push({
23719                 xtype :'MenuItem',
23720                 xns: Roo.bootstrap,
23721                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23722                 tagname : f,
23723                 listeners : {
23724                     click : function()
23725                     {
23726                         editorcore.insertTag(this.tagname);
23727                         editor.focus();
23728                     }
23729                 }
23730                 
23731             });
23732         });
23733         children.push(style);   
23734         
23735         btn('bold',false,true);
23736         btn('italic',false,true);
23737         btn('align-left', 'justifyleft',true);
23738         btn('align-center', 'justifycenter',true);
23739         btn('align-right' , 'justifyright',true);
23740         btn('link', false, false, function(btn) {
23741             //Roo.log("create link?");
23742             var url = prompt(this.createLinkText, this.defaultLinkValue);
23743             if(url && url != 'http:/'+'/'){
23744                 this.editorcore.relayCmd('createlink', url);
23745             }
23746         }),
23747         btn('list','insertunorderedlist',true);
23748         btn('pencil', false,true, function(btn){
23749                 Roo.log(this);
23750                 this.toggleSourceEdit(btn.pressed);
23751         });
23752         
23753         if (this.editor.btns.length > 0) {
23754             for (var i = 0; i<this.editor.btns.length; i++) {
23755                 children.push(this.editor.btns[i]);
23756             }
23757         }
23758         
23759         /*
23760         var cog = {
23761                 xtype: 'Button',
23762                 size : 'sm',
23763                 xns: Roo.bootstrap,
23764                 glyphicon : 'cog',
23765                 //html : 'submit'
23766                 menu : {
23767                     xtype: 'Menu',
23768                     xns: Roo.bootstrap,
23769                     items:  []
23770                 }
23771         };
23772         
23773         cog.menu.items.push({
23774             xtype :'MenuItem',
23775             xns: Roo.bootstrap,
23776             html : Clean styles,
23777             tagname : f,
23778             listeners : {
23779                 click : function()
23780                 {
23781                     editorcore.insertTag(this.tagname);
23782                     editor.focus();
23783                 }
23784             }
23785             
23786         });
23787        */
23788         
23789          
23790        this.xtype = 'NavSimplebar';
23791         
23792         for(var i=0;i< children.length;i++) {
23793             
23794             this.buttons.add(this.addxtypeChild(children[i]));
23795             
23796         }
23797         
23798         editor.on('editorevent', this.updateToolbar, this);
23799     },
23800     onBtnClick : function(id)
23801     {
23802        this.editorcore.relayCmd(id);
23803        this.editorcore.focus();
23804     },
23805     
23806     /**
23807      * Protected method that will not generally be called directly. It triggers
23808      * a toolbar update by reading the markup state of the current selection in the editor.
23809      */
23810     updateToolbar: function(){
23811
23812         if(!this.editorcore.activated){
23813             this.editor.onFirstFocus(); // is this neeed?
23814             return;
23815         }
23816
23817         var btns = this.buttons; 
23818         var doc = this.editorcore.doc;
23819         btns.get('bold').setActive(doc.queryCommandState('bold'));
23820         btns.get('italic').setActive(doc.queryCommandState('italic'));
23821         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23822         
23823         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23824         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23825         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23826         
23827         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23828         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23829          /*
23830         
23831         var ans = this.editorcore.getAllAncestors();
23832         if (this.formatCombo) {
23833             
23834             
23835             var store = this.formatCombo.store;
23836             this.formatCombo.setValue("");
23837             for (var i =0; i < ans.length;i++) {
23838                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23839                     // select it..
23840                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23841                     break;
23842                 }
23843             }
23844         }
23845         
23846         
23847         
23848         // hides menus... - so this cant be on a menu...
23849         Roo.bootstrap.MenuMgr.hideAll();
23850         */
23851         Roo.bootstrap.MenuMgr.hideAll();
23852         //this.editorsyncValue();
23853     },
23854     onFirstFocus: function() {
23855         this.buttons.each(function(item){
23856            item.enable();
23857         });
23858     },
23859     toggleSourceEdit : function(sourceEditMode){
23860         
23861           
23862         if(sourceEditMode){
23863             Roo.log("disabling buttons");
23864            this.buttons.each( function(item){
23865                 if(item.cmd != 'pencil'){
23866                     item.disable();
23867                 }
23868             });
23869           
23870         }else{
23871             Roo.log("enabling buttons");
23872             if(this.editorcore.initialized){
23873                 this.buttons.each( function(item){
23874                     item.enable();
23875                 });
23876             }
23877             
23878         }
23879         Roo.log("calling toggole on editor");
23880         // tell the editor that it's been pressed..
23881         this.editor.toggleSourceEdit(sourceEditMode);
23882        
23883     }
23884 });
23885
23886
23887
23888
23889
23890 /**
23891  * @class Roo.bootstrap.Table.AbstractSelectionModel
23892  * @extends Roo.util.Observable
23893  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23894  * implemented by descendant classes.  This class should not be directly instantiated.
23895  * @constructor
23896  */
23897 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23898     this.locked = false;
23899     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23900 };
23901
23902
23903 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23904     /** @ignore Called by the grid automatically. Do not call directly. */
23905     init : function(grid){
23906         this.grid = grid;
23907         this.initEvents();
23908     },
23909
23910     /**
23911      * Locks the selections.
23912      */
23913     lock : function(){
23914         this.locked = true;
23915     },
23916
23917     /**
23918      * Unlocks the selections.
23919      */
23920     unlock : function(){
23921         this.locked = false;
23922     },
23923
23924     /**
23925      * Returns true if the selections are locked.
23926      * @return {Boolean}
23927      */
23928     isLocked : function(){
23929         return this.locked;
23930     }
23931 });
23932 /**
23933  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23934  * @class Roo.bootstrap.Table.RowSelectionModel
23935  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23936  * It supports multiple selections and keyboard selection/navigation. 
23937  * @constructor
23938  * @param {Object} config
23939  */
23940
23941 Roo.bootstrap.Table.RowSelectionModel = function(config){
23942     Roo.apply(this, config);
23943     this.selections = new Roo.util.MixedCollection(false, function(o){
23944         return o.id;
23945     });
23946
23947     this.last = false;
23948     this.lastActive = false;
23949
23950     this.addEvents({
23951         /**
23952              * @event selectionchange
23953              * Fires when the selection changes
23954              * @param {SelectionModel} this
23955              */
23956             "selectionchange" : true,
23957         /**
23958              * @event afterselectionchange
23959              * Fires after the selection changes (eg. by key press or clicking)
23960              * @param {SelectionModel} this
23961              */
23962             "afterselectionchange" : true,
23963         /**
23964              * @event beforerowselect
23965              * Fires when a row is selected being selected, return false to cancel.
23966              * @param {SelectionModel} this
23967              * @param {Number} rowIndex The selected index
23968              * @param {Boolean} keepExisting False if other selections will be cleared
23969              */
23970             "beforerowselect" : true,
23971         /**
23972              * @event rowselect
23973              * Fires when a row is selected.
23974              * @param {SelectionModel} this
23975              * @param {Number} rowIndex The selected index
23976              * @param {Roo.data.Record} r The record
23977              */
23978             "rowselect" : true,
23979         /**
23980              * @event rowdeselect
23981              * Fires when a row is deselected.
23982              * @param {SelectionModel} this
23983              * @param {Number} rowIndex The selected index
23984              */
23985         "rowdeselect" : true
23986     });
23987     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23988     this.locked = false;
23989  };
23990
23991 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23992     /**
23993      * @cfg {Boolean} singleSelect
23994      * True to allow selection of only one row at a time (defaults to false)
23995      */
23996     singleSelect : false,
23997
23998     // private
23999     initEvents : function()
24000     {
24001
24002         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24003         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24004         //}else{ // allow click to work like normal
24005          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24006         //}
24007         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24008         this.grid.on("rowclick", this.handleMouseDown, this);
24009         
24010         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24011             "up" : function(e){
24012                 if(!e.shiftKey){
24013                     this.selectPrevious(e.shiftKey);
24014                 }else if(this.last !== false && this.lastActive !== false){
24015                     var last = this.last;
24016                     this.selectRange(this.last,  this.lastActive-1);
24017                     this.grid.getView().focusRow(this.lastActive);
24018                     if(last !== false){
24019                         this.last = last;
24020                     }
24021                 }else{
24022                     this.selectFirstRow();
24023                 }
24024                 this.fireEvent("afterselectionchange", this);
24025             },
24026             "down" : function(e){
24027                 if(!e.shiftKey){
24028                     this.selectNext(e.shiftKey);
24029                 }else if(this.last !== false && this.lastActive !== false){
24030                     var last = this.last;
24031                     this.selectRange(this.last,  this.lastActive+1);
24032                     this.grid.getView().focusRow(this.lastActive);
24033                     if(last !== false){
24034                         this.last = last;
24035                     }
24036                 }else{
24037                     this.selectFirstRow();
24038                 }
24039                 this.fireEvent("afterselectionchange", this);
24040             },
24041             scope: this
24042         });
24043         this.grid.store.on('load', function(){
24044             this.selections.clear();
24045         },this);
24046         /*
24047         var view = this.grid.view;
24048         view.on("refresh", this.onRefresh, this);
24049         view.on("rowupdated", this.onRowUpdated, this);
24050         view.on("rowremoved", this.onRemove, this);
24051         */
24052     },
24053
24054     // private
24055     onRefresh : function()
24056     {
24057         var ds = this.grid.store, i, v = this.grid.view;
24058         var s = this.selections;
24059         s.each(function(r){
24060             if((i = ds.indexOfId(r.id)) != -1){
24061                 v.onRowSelect(i);
24062             }else{
24063                 s.remove(r);
24064             }
24065         });
24066     },
24067
24068     // private
24069     onRemove : function(v, index, r){
24070         this.selections.remove(r);
24071     },
24072
24073     // private
24074     onRowUpdated : function(v, index, r){
24075         if(this.isSelected(r)){
24076             v.onRowSelect(index);
24077         }
24078     },
24079
24080     /**
24081      * Select records.
24082      * @param {Array} records The records to select
24083      * @param {Boolean} keepExisting (optional) True to keep existing selections
24084      */
24085     selectRecords : function(records, keepExisting)
24086     {
24087         if(!keepExisting){
24088             this.clearSelections();
24089         }
24090             var ds = this.grid.store;
24091         for(var i = 0, len = records.length; i < len; i++){
24092             this.selectRow(ds.indexOf(records[i]), true);
24093         }
24094     },
24095
24096     /**
24097      * Gets the number of selected rows.
24098      * @return {Number}
24099      */
24100     getCount : function(){
24101         return this.selections.length;
24102     },
24103
24104     /**
24105      * Selects the first row in the grid.
24106      */
24107     selectFirstRow : function(){
24108         this.selectRow(0);
24109     },
24110
24111     /**
24112      * Select the last row.
24113      * @param {Boolean} keepExisting (optional) True to keep existing selections
24114      */
24115     selectLastRow : function(keepExisting){
24116         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24117         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24118     },
24119
24120     /**
24121      * Selects the row immediately following the last selected row.
24122      * @param {Boolean} keepExisting (optional) True to keep existing selections
24123      */
24124     selectNext : function(keepExisting)
24125     {
24126             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24127             this.selectRow(this.last+1, keepExisting);
24128             this.grid.getView().focusRow(this.last);
24129         }
24130     },
24131
24132     /**
24133      * Selects the row that precedes the last selected row.
24134      * @param {Boolean} keepExisting (optional) True to keep existing selections
24135      */
24136     selectPrevious : function(keepExisting){
24137         if(this.last){
24138             this.selectRow(this.last-1, keepExisting);
24139             this.grid.getView().focusRow(this.last);
24140         }
24141     },
24142
24143     /**
24144      * Returns the selected records
24145      * @return {Array} Array of selected records
24146      */
24147     getSelections : function(){
24148         return [].concat(this.selections.items);
24149     },
24150
24151     /**
24152      * Returns the first selected record.
24153      * @return {Record}
24154      */
24155     getSelected : function(){
24156         return this.selections.itemAt(0);
24157     },
24158
24159
24160     /**
24161      * Clears all selections.
24162      */
24163     clearSelections : function(fast)
24164     {
24165         if(this.locked) {
24166             return;
24167         }
24168         if(fast !== true){
24169                 var ds = this.grid.store;
24170             var s = this.selections;
24171             s.each(function(r){
24172                 this.deselectRow(ds.indexOfId(r.id));
24173             }, this);
24174             s.clear();
24175         }else{
24176             this.selections.clear();
24177         }
24178         this.last = false;
24179     },
24180
24181
24182     /**
24183      * Selects all rows.
24184      */
24185     selectAll : function(){
24186         if(this.locked) {
24187             return;
24188         }
24189         this.selections.clear();
24190         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24191             this.selectRow(i, true);
24192         }
24193     },
24194
24195     /**
24196      * Returns True if there is a selection.
24197      * @return {Boolean}
24198      */
24199     hasSelection : function(){
24200         return this.selections.length > 0;
24201     },
24202
24203     /**
24204      * Returns True if the specified row is selected.
24205      * @param {Number/Record} record The record or index of the record to check
24206      * @return {Boolean}
24207      */
24208     isSelected : function(index){
24209             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24210         return (r && this.selections.key(r.id) ? true : false);
24211     },
24212
24213     /**
24214      * Returns True if the specified record id is selected.
24215      * @param {String} id The id of record to check
24216      * @return {Boolean}
24217      */
24218     isIdSelected : function(id){
24219         return (this.selections.key(id) ? true : false);
24220     },
24221
24222
24223     // private
24224     handleMouseDBClick : function(e, t){
24225         
24226     },
24227     // private
24228     handleMouseDown : function(e, t)
24229     {
24230             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24231         if(this.isLocked() || rowIndex < 0 ){
24232             return;
24233         };
24234         if(e.shiftKey && this.last !== false){
24235             var last = this.last;
24236             this.selectRange(last, rowIndex, e.ctrlKey);
24237             this.last = last; // reset the last
24238             t.focus();
24239     
24240         }else{
24241             var isSelected = this.isSelected(rowIndex);
24242             //Roo.log("select row:" + rowIndex);
24243             if(isSelected){
24244                 this.deselectRow(rowIndex);
24245             } else {
24246                         this.selectRow(rowIndex, true);
24247             }
24248     
24249             /*
24250                 if(e.button !== 0 && isSelected){
24251                 alert('rowIndex 2: ' + rowIndex);
24252                     view.focusRow(rowIndex);
24253                 }else if(e.ctrlKey && isSelected){
24254                     this.deselectRow(rowIndex);
24255                 }else if(!isSelected){
24256                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24257                     view.focusRow(rowIndex);
24258                 }
24259             */
24260         }
24261         this.fireEvent("afterselectionchange", this);
24262     },
24263     // private
24264     handleDragableRowClick :  function(grid, rowIndex, e) 
24265     {
24266         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24267             this.selectRow(rowIndex, false);
24268             grid.view.focusRow(rowIndex);
24269              this.fireEvent("afterselectionchange", this);
24270         }
24271     },
24272     
24273     /**
24274      * Selects multiple rows.
24275      * @param {Array} rows Array of the indexes of the row to select
24276      * @param {Boolean} keepExisting (optional) True to keep existing selections
24277      */
24278     selectRows : function(rows, keepExisting){
24279         if(!keepExisting){
24280             this.clearSelections();
24281         }
24282         for(var i = 0, len = rows.length; i < len; i++){
24283             this.selectRow(rows[i], true);
24284         }
24285     },
24286
24287     /**
24288      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24289      * @param {Number} startRow The index of the first row in the range
24290      * @param {Number} endRow The index of the last row in the range
24291      * @param {Boolean} keepExisting (optional) True to retain existing selections
24292      */
24293     selectRange : function(startRow, endRow, keepExisting){
24294         if(this.locked) {
24295             return;
24296         }
24297         if(!keepExisting){
24298             this.clearSelections();
24299         }
24300         if(startRow <= endRow){
24301             for(var i = startRow; i <= endRow; i++){
24302                 this.selectRow(i, true);
24303             }
24304         }else{
24305             for(var i = startRow; i >= endRow; i--){
24306                 this.selectRow(i, true);
24307             }
24308         }
24309     },
24310
24311     /**
24312      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24313      * @param {Number} startRow The index of the first row in the range
24314      * @param {Number} endRow The index of the last row in the range
24315      */
24316     deselectRange : function(startRow, endRow, preventViewNotify){
24317         if(this.locked) {
24318             return;
24319         }
24320         for(var i = startRow; i <= endRow; i++){
24321             this.deselectRow(i, preventViewNotify);
24322         }
24323     },
24324
24325     /**
24326      * Selects a row.
24327      * @param {Number} row The index of the row to select
24328      * @param {Boolean} keepExisting (optional) True to keep existing selections
24329      */
24330     selectRow : function(index, keepExisting, preventViewNotify)
24331     {
24332             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24333             return;
24334         }
24335         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24336             if(!keepExisting || this.singleSelect){
24337                 this.clearSelections();
24338             }
24339             
24340             var r = this.grid.store.getAt(index);
24341             //console.log('selectRow - record id :' + r.id);
24342             
24343             this.selections.add(r);
24344             this.last = this.lastActive = index;
24345             if(!preventViewNotify){
24346                 var proxy = new Roo.Element(
24347                                 this.grid.getRowDom(index)
24348                 );
24349                 proxy.addClass('bg-info info');
24350             }
24351             this.fireEvent("rowselect", this, index, r);
24352             this.fireEvent("selectionchange", this);
24353         }
24354     },
24355
24356     /**
24357      * Deselects a row.
24358      * @param {Number} row The index of the row to deselect
24359      */
24360     deselectRow : function(index, preventViewNotify)
24361     {
24362         if(this.locked) {
24363             return;
24364         }
24365         if(this.last == index){
24366             this.last = false;
24367         }
24368         if(this.lastActive == index){
24369             this.lastActive = false;
24370         }
24371         
24372         var r = this.grid.store.getAt(index);
24373         if (!r) {
24374             return;
24375         }
24376         
24377         this.selections.remove(r);
24378         //.console.log('deselectRow - record id :' + r.id);
24379         if(!preventViewNotify){
24380         
24381             var proxy = new Roo.Element(
24382                 this.grid.getRowDom(index)
24383             );
24384             proxy.removeClass('bg-info info');
24385         }
24386         this.fireEvent("rowdeselect", this, index);
24387         this.fireEvent("selectionchange", this);
24388     },
24389
24390     // private
24391     restoreLast : function(){
24392         if(this._last){
24393             this.last = this._last;
24394         }
24395     },
24396
24397     // private
24398     acceptsNav : function(row, col, cm){
24399         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24400     },
24401
24402     // private
24403     onEditorKey : function(field, e){
24404         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24405         if(k == e.TAB){
24406             e.stopEvent();
24407             ed.completeEdit();
24408             if(e.shiftKey){
24409                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24410             }else{
24411                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24412             }
24413         }else if(k == e.ENTER && !e.ctrlKey){
24414             e.stopEvent();
24415             ed.completeEdit();
24416             if(e.shiftKey){
24417                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24418             }else{
24419                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24420             }
24421         }else if(k == e.ESC){
24422             ed.cancelEdit();
24423         }
24424         if(newCell){
24425             g.startEditing(newCell[0], newCell[1]);
24426         }
24427     }
24428 });
24429 /*
24430  * Based on:
24431  * Ext JS Library 1.1.1
24432  * Copyright(c) 2006-2007, Ext JS, LLC.
24433  *
24434  * Originally Released Under LGPL - original licence link has changed is not relivant.
24435  *
24436  * Fork - LGPL
24437  * <script type="text/javascript">
24438  */
24439  
24440 /**
24441  * @class Roo.bootstrap.PagingToolbar
24442  * @extends Roo.bootstrap.NavSimplebar
24443  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24444  * @constructor
24445  * Create a new PagingToolbar
24446  * @param {Object} config The config object
24447  * @param {Roo.data.Store} store
24448  */
24449 Roo.bootstrap.PagingToolbar = function(config)
24450 {
24451     // old args format still supported... - xtype is prefered..
24452         // created from xtype...
24453     
24454     this.ds = config.dataSource;
24455     
24456     if (config.store && !this.ds) {
24457         this.store= Roo.factory(config.store, Roo.data);
24458         this.ds = this.store;
24459         this.ds.xmodule = this.xmodule || false;
24460     }
24461     
24462     this.toolbarItems = [];
24463     if (config.items) {
24464         this.toolbarItems = config.items;
24465     }
24466     
24467     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24468     
24469     this.cursor = 0;
24470     
24471     if (this.ds) { 
24472         this.bind(this.ds);
24473     }
24474     
24475     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24476     
24477 };
24478
24479 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24480     /**
24481      * @cfg {Roo.data.Store} dataSource
24482      * The underlying data store providing the paged data
24483      */
24484     /**
24485      * @cfg {String/HTMLElement/Element} container
24486      * container The id or element that will contain the toolbar
24487      */
24488     /**
24489      * @cfg {Boolean} displayInfo
24490      * True to display the displayMsg (defaults to false)
24491      */
24492     /**
24493      * @cfg {Number} pageSize
24494      * The number of records to display per page (defaults to 20)
24495      */
24496     pageSize: 20,
24497     /**
24498      * @cfg {String} displayMsg
24499      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24500      */
24501     displayMsg : 'Displaying {0} - {1} of {2}',
24502     /**
24503      * @cfg {String} emptyMsg
24504      * The message to display when no records are found (defaults to "No data to display")
24505      */
24506     emptyMsg : 'No data to display',
24507     /**
24508      * Customizable piece of the default paging text (defaults to "Page")
24509      * @type String
24510      */
24511     beforePageText : "Page",
24512     /**
24513      * Customizable piece of the default paging text (defaults to "of %0")
24514      * @type String
24515      */
24516     afterPageText : "of {0}",
24517     /**
24518      * Customizable piece of the default paging text (defaults to "First Page")
24519      * @type String
24520      */
24521     firstText : "First Page",
24522     /**
24523      * Customizable piece of the default paging text (defaults to "Previous Page")
24524      * @type String
24525      */
24526     prevText : "Previous Page",
24527     /**
24528      * Customizable piece of the default paging text (defaults to "Next Page")
24529      * @type String
24530      */
24531     nextText : "Next Page",
24532     /**
24533      * Customizable piece of the default paging text (defaults to "Last Page")
24534      * @type String
24535      */
24536     lastText : "Last Page",
24537     /**
24538      * Customizable piece of the default paging text (defaults to "Refresh")
24539      * @type String
24540      */
24541     refreshText : "Refresh",
24542
24543     buttons : false,
24544     // private
24545     onRender : function(ct, position) 
24546     {
24547         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24548         this.navgroup.parentId = this.id;
24549         this.navgroup.onRender(this.el, null);
24550         // add the buttons to the navgroup
24551         
24552         if(this.displayInfo){
24553             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24554             this.displayEl = this.el.select('.x-paging-info', true).first();
24555 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24556 //            this.displayEl = navel.el.select('span',true).first();
24557         }
24558         
24559         var _this = this;
24560         
24561         if(this.buttons){
24562             Roo.each(_this.buttons, function(e){ // this might need to use render????
24563                Roo.factory(e).render(_this.el);
24564             });
24565         }
24566             
24567         Roo.each(_this.toolbarItems, function(e) {
24568             _this.navgroup.addItem(e);
24569         });
24570         
24571         
24572         this.first = this.navgroup.addItem({
24573             tooltip: this.firstText,
24574             cls: "prev",
24575             icon : 'fa fa-backward',
24576             disabled: true,
24577             preventDefault: true,
24578             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24579         });
24580         
24581         this.prev =  this.navgroup.addItem({
24582             tooltip: this.prevText,
24583             cls: "prev",
24584             icon : 'fa fa-step-backward',
24585             disabled: true,
24586             preventDefault: true,
24587             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24588         });
24589     //this.addSeparator();
24590         
24591         
24592         var field = this.navgroup.addItem( {
24593             tagtype : 'span',
24594             cls : 'x-paging-position',
24595             
24596             html : this.beforePageText  +
24597                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24598                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24599          } ); //?? escaped?
24600         
24601         this.field = field.el.select('input', true).first();
24602         this.field.on("keydown", this.onPagingKeydown, this);
24603         this.field.on("focus", function(){this.dom.select();});
24604     
24605     
24606         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24607         //this.field.setHeight(18);
24608         //this.addSeparator();
24609         this.next = this.navgroup.addItem({
24610             tooltip: this.nextText,
24611             cls: "next",
24612             html : ' <i class="fa fa-step-forward">',
24613             disabled: true,
24614             preventDefault: true,
24615             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24616         });
24617         this.last = this.navgroup.addItem({
24618             tooltip: this.lastText,
24619             icon : 'fa fa-forward',
24620             cls: "next",
24621             disabled: true,
24622             preventDefault: true,
24623             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24624         });
24625     //this.addSeparator();
24626         this.loading = this.navgroup.addItem({
24627             tooltip: this.refreshText,
24628             icon: 'fa fa-refresh',
24629             preventDefault: true,
24630             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24631         });
24632         
24633     },
24634
24635     // private
24636     updateInfo : function(){
24637         if(this.displayEl){
24638             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24639             var msg = count == 0 ?
24640                 this.emptyMsg :
24641                 String.format(
24642                     this.displayMsg,
24643                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24644                 );
24645             this.displayEl.update(msg);
24646         }
24647     },
24648
24649     // private
24650     onLoad : function(ds, r, o)
24651     {
24652         this.cursor = o.params.start ? o.params.start : 0;
24653         
24654         var d = this.getPageData(),
24655             ap = d.activePage,
24656             ps = d.pages;
24657         
24658         
24659         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24660         this.field.dom.value = ap;
24661         this.first.setDisabled(ap == 1);
24662         this.prev.setDisabled(ap == 1);
24663         this.next.setDisabled(ap == ps);
24664         this.last.setDisabled(ap == ps);
24665         this.loading.enable();
24666         this.updateInfo();
24667     },
24668
24669     // private
24670     getPageData : function(){
24671         var total = this.ds.getTotalCount();
24672         return {
24673             total : total,
24674             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24675             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24676         };
24677     },
24678
24679     // private
24680     onLoadError : function(){
24681         this.loading.enable();
24682     },
24683
24684     // private
24685     onPagingKeydown : function(e){
24686         var k = e.getKey();
24687         var d = this.getPageData();
24688         if(k == e.RETURN){
24689             var v = this.field.dom.value, pageNum;
24690             if(!v || isNaN(pageNum = parseInt(v, 10))){
24691                 this.field.dom.value = d.activePage;
24692                 return;
24693             }
24694             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24695             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24696             e.stopEvent();
24697         }
24698         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))
24699         {
24700           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24701           this.field.dom.value = pageNum;
24702           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24703           e.stopEvent();
24704         }
24705         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24706         {
24707           var v = this.field.dom.value, pageNum; 
24708           var increment = (e.shiftKey) ? 10 : 1;
24709           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24710                 increment *= -1;
24711           }
24712           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24713             this.field.dom.value = d.activePage;
24714             return;
24715           }
24716           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24717           {
24718             this.field.dom.value = parseInt(v, 10) + increment;
24719             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24720             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24721           }
24722           e.stopEvent();
24723         }
24724     },
24725
24726     // private
24727     beforeLoad : function(){
24728         if(this.loading){
24729             this.loading.disable();
24730         }
24731     },
24732
24733     // private
24734     onClick : function(which){
24735         
24736         var ds = this.ds;
24737         if (!ds) {
24738             return;
24739         }
24740         
24741         switch(which){
24742             case "first":
24743                 ds.load({params:{start: 0, limit: this.pageSize}});
24744             break;
24745             case "prev":
24746                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24747             break;
24748             case "next":
24749                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24750             break;
24751             case "last":
24752                 var total = ds.getTotalCount();
24753                 var extra = total % this.pageSize;
24754                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24755                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24756             break;
24757             case "refresh":
24758                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24759             break;
24760         }
24761     },
24762
24763     /**
24764      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24765      * @param {Roo.data.Store} store The data store to unbind
24766      */
24767     unbind : function(ds){
24768         ds.un("beforeload", this.beforeLoad, this);
24769         ds.un("load", this.onLoad, this);
24770         ds.un("loadexception", this.onLoadError, this);
24771         ds.un("remove", this.updateInfo, this);
24772         ds.un("add", this.updateInfo, this);
24773         this.ds = undefined;
24774     },
24775
24776     /**
24777      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24778      * @param {Roo.data.Store} store The data store to bind
24779      */
24780     bind : function(ds){
24781         ds.on("beforeload", this.beforeLoad, this);
24782         ds.on("load", this.onLoad, this);
24783         ds.on("loadexception", this.onLoadError, this);
24784         ds.on("remove", this.updateInfo, this);
24785         ds.on("add", this.updateInfo, this);
24786         this.ds = ds;
24787     }
24788 });/*
24789  * - LGPL
24790  *
24791  * element
24792  * 
24793  */
24794
24795 /**
24796  * @class Roo.bootstrap.MessageBar
24797  * @extends Roo.bootstrap.Component
24798  * Bootstrap MessageBar class
24799  * @cfg {String} html contents of the MessageBar
24800  * @cfg {String} weight (info | success | warning | danger) default info
24801  * @cfg {String} beforeClass insert the bar before the given class
24802  * @cfg {Boolean} closable (true | false) default false
24803  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24804  * 
24805  * @constructor
24806  * Create a new Element
24807  * @param {Object} config The config object
24808  */
24809
24810 Roo.bootstrap.MessageBar = function(config){
24811     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24812 };
24813
24814 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24815     
24816     html: '',
24817     weight: 'info',
24818     closable: false,
24819     fixed: false,
24820     beforeClass: 'bootstrap-sticky-wrap',
24821     
24822     getAutoCreate : function(){
24823         
24824         var cfg = {
24825             tag: 'div',
24826             cls: 'alert alert-dismissable alert-' + this.weight,
24827             cn: [
24828                 {
24829                     tag: 'span',
24830                     cls: 'message',
24831                     html: this.html || ''
24832                 }
24833             ]
24834         };
24835         
24836         if(this.fixed){
24837             cfg.cls += ' alert-messages-fixed';
24838         }
24839         
24840         if(this.closable){
24841             cfg.cn.push({
24842                 tag: 'button',
24843                 cls: 'close',
24844                 html: 'x'
24845             });
24846         }
24847         
24848         return cfg;
24849     },
24850     
24851     onRender : function(ct, position)
24852     {
24853         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24854         
24855         if(!this.el){
24856             var cfg = Roo.apply({},  this.getAutoCreate());
24857             cfg.id = Roo.id();
24858             
24859             if (this.cls) {
24860                 cfg.cls += ' ' + this.cls;
24861             }
24862             if (this.style) {
24863                 cfg.style = this.style;
24864             }
24865             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24866             
24867             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24868         }
24869         
24870         this.el.select('>button.close').on('click', this.hide, this);
24871         
24872     },
24873     
24874     show : function()
24875     {
24876         if (!this.rendered) {
24877             this.render();
24878         }
24879         
24880         this.el.show();
24881         
24882         this.fireEvent('show', this);
24883         
24884     },
24885     
24886     hide : function()
24887     {
24888         if (!this.rendered) {
24889             this.render();
24890         }
24891         
24892         this.el.hide();
24893         
24894         this.fireEvent('hide', this);
24895     },
24896     
24897     update : function()
24898     {
24899 //        var e = this.el.dom.firstChild;
24900 //        
24901 //        if(this.closable){
24902 //            e = e.nextSibling;
24903 //        }
24904 //        
24905 //        e.data = this.html || '';
24906
24907         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24908     }
24909    
24910 });
24911
24912  
24913
24914      /*
24915  * - LGPL
24916  *
24917  * Graph
24918  * 
24919  */
24920
24921
24922 /**
24923  * @class Roo.bootstrap.Graph
24924  * @extends Roo.bootstrap.Component
24925  * Bootstrap Graph class
24926 > Prameters
24927  -sm {number} sm 4
24928  -md {number} md 5
24929  @cfg {String} graphtype  bar | vbar | pie
24930  @cfg {number} g_x coodinator | centre x (pie)
24931  @cfg {number} g_y coodinator | centre y (pie)
24932  @cfg {number} g_r radius (pie)
24933  @cfg {number} g_height height of the chart (respected by all elements in the set)
24934  @cfg {number} g_width width of the chart (respected by all elements in the set)
24935  @cfg {Object} title The title of the chart
24936     
24937  -{Array}  values
24938  -opts (object) options for the chart 
24939      o {
24940      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24941      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24942      o vgutter (number)
24943      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.
24944      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24945      o to
24946      o stretch (boolean)
24947      o }
24948  -opts (object) options for the pie
24949      o{
24950      o cut
24951      o startAngle (number)
24952      o endAngle (number)
24953      } 
24954  *
24955  * @constructor
24956  * Create a new Input
24957  * @param {Object} config The config object
24958  */
24959
24960 Roo.bootstrap.Graph = function(config){
24961     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24962     
24963     this.addEvents({
24964         // img events
24965         /**
24966          * @event click
24967          * The img click event for the img.
24968          * @param {Roo.EventObject} e
24969          */
24970         "click" : true
24971     });
24972 };
24973
24974 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24975     
24976     sm: 4,
24977     md: 5,
24978     graphtype: 'bar',
24979     g_height: 250,
24980     g_width: 400,
24981     g_x: 50,
24982     g_y: 50,
24983     g_r: 30,
24984     opts:{
24985         //g_colors: this.colors,
24986         g_type: 'soft',
24987         g_gutter: '20%'
24988
24989     },
24990     title : false,
24991
24992     getAutoCreate : function(){
24993         
24994         var cfg = {
24995             tag: 'div',
24996             html : null
24997         };
24998         
24999         
25000         return  cfg;
25001     },
25002
25003     onRender : function(ct,position){
25004         
25005         
25006         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25007         
25008         if (typeof(Raphael) == 'undefined') {
25009             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25010             return;
25011         }
25012         
25013         this.raphael = Raphael(this.el.dom);
25014         
25015                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25016                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25017                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25018                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25019                 /*
25020                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25021                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25022                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25023                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25024                 
25025                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25026                 r.barchart(330, 10, 300, 220, data1);
25027                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25028                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25029                 */
25030                 
25031                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25032                 // r.barchart(30, 30, 560, 250,  xdata, {
25033                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25034                 //     axis : "0 0 1 1",
25035                 //     axisxlabels :  xdata
25036                 //     //yvalues : cols,
25037                    
25038                 // });
25039 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25040 //        
25041 //        this.load(null,xdata,{
25042 //                axis : "0 0 1 1",
25043 //                axisxlabels :  xdata
25044 //                });
25045
25046     },
25047
25048     load : function(graphtype,xdata,opts)
25049     {
25050         this.raphael.clear();
25051         if(!graphtype) {
25052             graphtype = this.graphtype;
25053         }
25054         if(!opts){
25055             opts = this.opts;
25056         }
25057         var r = this.raphael,
25058             fin = function () {
25059                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25060             },
25061             fout = function () {
25062                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25063             },
25064             pfin = function() {
25065                 this.sector.stop();
25066                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25067
25068                 if (this.label) {
25069                     this.label[0].stop();
25070                     this.label[0].attr({ r: 7.5 });
25071                     this.label[1].attr({ "font-weight": 800 });
25072                 }
25073             },
25074             pfout = function() {
25075                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25076
25077                 if (this.label) {
25078                     this.label[0].animate({ r: 5 }, 500, "bounce");
25079                     this.label[1].attr({ "font-weight": 400 });
25080                 }
25081             };
25082
25083         switch(graphtype){
25084             case 'bar':
25085                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25086                 break;
25087             case 'hbar':
25088                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25089                 break;
25090             case 'pie':
25091 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25092 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25093 //            
25094                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25095                 
25096                 break;
25097
25098         }
25099         
25100         if(this.title){
25101             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25102         }
25103         
25104     },
25105     
25106     setTitle: function(o)
25107     {
25108         this.title = o;
25109     },
25110     
25111     initEvents: function() {
25112         
25113         if(!this.href){
25114             this.el.on('click', this.onClick, this);
25115         }
25116     },
25117     
25118     onClick : function(e)
25119     {
25120         Roo.log('img onclick');
25121         this.fireEvent('click', this, e);
25122     }
25123    
25124 });
25125
25126  
25127 /*
25128  * - LGPL
25129  *
25130  * numberBox
25131  * 
25132  */
25133 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25134
25135 /**
25136  * @class Roo.bootstrap.dash.NumberBox
25137  * @extends Roo.bootstrap.Component
25138  * Bootstrap NumberBox class
25139  * @cfg {String} headline Box headline
25140  * @cfg {String} content Box content
25141  * @cfg {String} icon Box icon
25142  * @cfg {String} footer Footer text
25143  * @cfg {String} fhref Footer href
25144  * 
25145  * @constructor
25146  * Create a new NumberBox
25147  * @param {Object} config The config object
25148  */
25149
25150
25151 Roo.bootstrap.dash.NumberBox = function(config){
25152     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25153     
25154 };
25155
25156 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25157     
25158     headline : '',
25159     content : '',
25160     icon : '',
25161     footer : '',
25162     fhref : '',
25163     ficon : '',
25164     
25165     getAutoCreate : function(){
25166         
25167         var cfg = {
25168             tag : 'div',
25169             cls : 'small-box ',
25170             cn : [
25171                 {
25172                     tag : 'div',
25173                     cls : 'inner',
25174                     cn :[
25175                         {
25176                             tag : 'h3',
25177                             cls : 'roo-headline',
25178                             html : this.headline
25179                         },
25180                         {
25181                             tag : 'p',
25182                             cls : 'roo-content',
25183                             html : this.content
25184                         }
25185                     ]
25186                 }
25187             ]
25188         };
25189         
25190         if(this.icon){
25191             cfg.cn.push({
25192                 tag : 'div',
25193                 cls : 'icon',
25194                 cn :[
25195                     {
25196                         tag : 'i',
25197                         cls : 'ion ' + this.icon
25198                     }
25199                 ]
25200             });
25201         }
25202         
25203         if(this.footer){
25204             var footer = {
25205                 tag : 'a',
25206                 cls : 'small-box-footer',
25207                 href : this.fhref || '#',
25208                 html : this.footer
25209             };
25210             
25211             cfg.cn.push(footer);
25212             
25213         }
25214         
25215         return  cfg;
25216     },
25217
25218     onRender : function(ct,position){
25219         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25220
25221
25222        
25223                 
25224     },
25225
25226     setHeadline: function (value)
25227     {
25228         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25229     },
25230     
25231     setFooter: function (value, href)
25232     {
25233         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25234         
25235         if(href){
25236             this.el.select('a.small-box-footer',true).first().attr('href', href);
25237         }
25238         
25239     },
25240
25241     setContent: function (value)
25242     {
25243         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25244     },
25245
25246     initEvents: function() 
25247     {   
25248         
25249     }
25250     
25251 });
25252
25253  
25254 /*
25255  * - LGPL
25256  *
25257  * TabBox
25258  * 
25259  */
25260 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25261
25262 /**
25263  * @class Roo.bootstrap.dash.TabBox
25264  * @extends Roo.bootstrap.Component
25265  * Bootstrap TabBox class
25266  * @cfg {String} title Title of the TabBox
25267  * @cfg {String} icon Icon of the TabBox
25268  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25269  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25270  * 
25271  * @constructor
25272  * Create a new TabBox
25273  * @param {Object} config The config object
25274  */
25275
25276
25277 Roo.bootstrap.dash.TabBox = function(config){
25278     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25279     this.addEvents({
25280         // raw events
25281         /**
25282          * @event addpane
25283          * When a pane is added
25284          * @param {Roo.bootstrap.dash.TabPane} pane
25285          */
25286         "addpane" : true,
25287         /**
25288          * @event activatepane
25289          * When a pane is activated
25290          * @param {Roo.bootstrap.dash.TabPane} pane
25291          */
25292         "activatepane" : true
25293         
25294          
25295     });
25296     
25297     this.panes = [];
25298 };
25299
25300 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25301
25302     title : '',
25303     icon : false,
25304     showtabs : true,
25305     tabScrollable : false,
25306     
25307     getChildContainer : function()
25308     {
25309         return this.el.select('.tab-content', true).first();
25310     },
25311     
25312     getAutoCreate : function(){
25313         
25314         var header = {
25315             tag: 'li',
25316             cls: 'pull-left header',
25317             html: this.title,
25318             cn : []
25319         };
25320         
25321         if(this.icon){
25322             header.cn.push({
25323                 tag: 'i',
25324                 cls: 'fa ' + this.icon
25325             });
25326         }
25327         
25328         var h = {
25329             tag: 'ul',
25330             cls: 'nav nav-tabs pull-right',
25331             cn: [
25332                 header
25333             ]
25334         };
25335         
25336         if(this.tabScrollable){
25337             h = {
25338                 tag: 'div',
25339                 cls: 'tab-header',
25340                 cn: [
25341                     {
25342                         tag: 'ul',
25343                         cls: 'nav nav-tabs pull-right',
25344                         cn: [
25345                             header
25346                         ]
25347                     }
25348                 ]
25349             };
25350         }
25351         
25352         var cfg = {
25353             tag: 'div',
25354             cls: 'nav-tabs-custom',
25355             cn: [
25356                 h,
25357                 {
25358                     tag: 'div',
25359                     cls: 'tab-content no-padding',
25360                     cn: []
25361                 }
25362             ]
25363         };
25364
25365         return  cfg;
25366     },
25367     initEvents : function()
25368     {
25369         //Roo.log('add add pane handler');
25370         this.on('addpane', this.onAddPane, this);
25371     },
25372      /**
25373      * Updates the box title
25374      * @param {String} html to set the title to.
25375      */
25376     setTitle : function(value)
25377     {
25378         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25379     },
25380     onAddPane : function(pane)
25381     {
25382         this.panes.push(pane);
25383         //Roo.log('addpane');
25384         //Roo.log(pane);
25385         // tabs are rendere left to right..
25386         if(!this.showtabs){
25387             return;
25388         }
25389         
25390         var ctr = this.el.select('.nav-tabs', true).first();
25391          
25392          
25393         var existing = ctr.select('.nav-tab',true);
25394         var qty = existing.getCount();;
25395         
25396         
25397         var tab = ctr.createChild({
25398             tag : 'li',
25399             cls : 'nav-tab' + (qty ? '' : ' active'),
25400             cn : [
25401                 {
25402                     tag : 'a',
25403                     href:'#',
25404                     html : pane.title
25405                 }
25406             ]
25407         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25408         pane.tab = tab;
25409         
25410         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25411         if (!qty) {
25412             pane.el.addClass('active');
25413         }
25414         
25415                 
25416     },
25417     onTabClick : function(ev,un,ob,pane)
25418     {
25419         //Roo.log('tab - prev default');
25420         ev.preventDefault();
25421         
25422         
25423         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25424         pane.tab.addClass('active');
25425         //Roo.log(pane.title);
25426         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25427         // technically we should have a deactivate event.. but maybe add later.
25428         // and it should not de-activate the selected tab...
25429         this.fireEvent('activatepane', pane);
25430         pane.el.addClass('active');
25431         pane.fireEvent('activate');
25432         
25433         
25434     },
25435     
25436     getActivePane : function()
25437     {
25438         var r = false;
25439         Roo.each(this.panes, function(p) {
25440             if(p.el.hasClass('active')){
25441                 r = p;
25442                 return false;
25443             }
25444             
25445             return;
25446         });
25447         
25448         return r;
25449     }
25450     
25451     
25452 });
25453
25454  
25455 /*
25456  * - LGPL
25457  *
25458  * Tab pane
25459  * 
25460  */
25461 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25462 /**
25463  * @class Roo.bootstrap.TabPane
25464  * @extends Roo.bootstrap.Component
25465  * Bootstrap TabPane class
25466  * @cfg {Boolean} active (false | true) Default false
25467  * @cfg {String} title title of panel
25468
25469  * 
25470  * @constructor
25471  * Create a new TabPane
25472  * @param {Object} config The config object
25473  */
25474
25475 Roo.bootstrap.dash.TabPane = function(config){
25476     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25477     
25478     this.addEvents({
25479         // raw events
25480         /**
25481          * @event activate
25482          * When a pane is activated
25483          * @param {Roo.bootstrap.dash.TabPane} pane
25484          */
25485         "activate" : true
25486          
25487     });
25488 };
25489
25490 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25491     
25492     active : false,
25493     title : '',
25494     
25495     // the tabBox that this is attached to.
25496     tab : false,
25497      
25498     getAutoCreate : function() 
25499     {
25500         var cfg = {
25501             tag: 'div',
25502             cls: 'tab-pane'
25503         };
25504         
25505         if(this.active){
25506             cfg.cls += ' active';
25507         }
25508         
25509         return cfg;
25510     },
25511     initEvents  : function()
25512     {
25513         //Roo.log('trigger add pane handler');
25514         this.parent().fireEvent('addpane', this)
25515     },
25516     
25517      /**
25518      * Updates the tab title 
25519      * @param {String} html to set the title to.
25520      */
25521     setTitle: function(str)
25522     {
25523         if (!this.tab) {
25524             return;
25525         }
25526         this.title = str;
25527         this.tab.select('a', true).first().dom.innerHTML = str;
25528         
25529     }
25530     
25531     
25532     
25533 });
25534
25535  
25536
25537
25538  /*
25539  * - LGPL
25540  *
25541  * menu
25542  * 
25543  */
25544 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25545
25546 /**
25547  * @class Roo.bootstrap.menu.Menu
25548  * @extends Roo.bootstrap.Component
25549  * Bootstrap Menu class - container for Menu
25550  * @cfg {String} html Text of the menu
25551  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25552  * @cfg {String} icon Font awesome icon
25553  * @cfg {String} pos Menu align to (top | bottom) default bottom
25554  * 
25555  * 
25556  * @constructor
25557  * Create a new Menu
25558  * @param {Object} config The config object
25559  */
25560
25561
25562 Roo.bootstrap.menu.Menu = function(config){
25563     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25564     
25565     this.addEvents({
25566         /**
25567          * @event beforeshow
25568          * Fires before this menu is displayed
25569          * @param {Roo.bootstrap.menu.Menu} this
25570          */
25571         beforeshow : true,
25572         /**
25573          * @event beforehide
25574          * Fires before this menu is hidden
25575          * @param {Roo.bootstrap.menu.Menu} this
25576          */
25577         beforehide : true,
25578         /**
25579          * @event show
25580          * Fires after this menu is displayed
25581          * @param {Roo.bootstrap.menu.Menu} this
25582          */
25583         show : true,
25584         /**
25585          * @event hide
25586          * Fires after this menu is hidden
25587          * @param {Roo.bootstrap.menu.Menu} this
25588          */
25589         hide : true,
25590         /**
25591          * @event click
25592          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25593          * @param {Roo.bootstrap.menu.Menu} this
25594          * @param {Roo.EventObject} e
25595          */
25596         click : true
25597     });
25598     
25599 };
25600
25601 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25602     
25603     submenu : false,
25604     html : '',
25605     weight : 'default',
25606     icon : false,
25607     pos : 'bottom',
25608     
25609     
25610     getChildContainer : function() {
25611         if(this.isSubMenu){
25612             return this.el;
25613         }
25614         
25615         return this.el.select('ul.dropdown-menu', true).first();  
25616     },
25617     
25618     getAutoCreate : function()
25619     {
25620         var text = [
25621             {
25622                 tag : 'span',
25623                 cls : 'roo-menu-text',
25624                 html : this.html
25625             }
25626         ];
25627         
25628         if(this.icon){
25629             text.unshift({
25630                 tag : 'i',
25631                 cls : 'fa ' + this.icon
25632             })
25633         }
25634         
25635         
25636         var cfg = {
25637             tag : 'div',
25638             cls : 'btn-group',
25639             cn : [
25640                 {
25641                     tag : 'button',
25642                     cls : 'dropdown-button btn btn-' + this.weight,
25643                     cn : text
25644                 },
25645                 {
25646                     tag : 'button',
25647                     cls : 'dropdown-toggle btn btn-' + this.weight,
25648                     cn : [
25649                         {
25650                             tag : 'span',
25651                             cls : 'caret'
25652                         }
25653                     ]
25654                 },
25655                 {
25656                     tag : 'ul',
25657                     cls : 'dropdown-menu'
25658                 }
25659             ]
25660             
25661         };
25662         
25663         if(this.pos == 'top'){
25664             cfg.cls += ' dropup';
25665         }
25666         
25667         if(this.isSubMenu){
25668             cfg = {
25669                 tag : 'ul',
25670                 cls : 'dropdown-menu'
25671             }
25672         }
25673         
25674         return cfg;
25675     },
25676     
25677     onRender : function(ct, position)
25678     {
25679         this.isSubMenu = ct.hasClass('dropdown-submenu');
25680         
25681         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25682     },
25683     
25684     initEvents : function() 
25685     {
25686         if(this.isSubMenu){
25687             return;
25688         }
25689         
25690         this.hidden = true;
25691         
25692         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25693         this.triggerEl.on('click', this.onTriggerPress, this);
25694         
25695         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25696         this.buttonEl.on('click', this.onClick, this);
25697         
25698     },
25699     
25700     list : function()
25701     {
25702         if(this.isSubMenu){
25703             return this.el;
25704         }
25705         
25706         return this.el.select('ul.dropdown-menu', true).first();
25707     },
25708     
25709     onClick : function(e)
25710     {
25711         this.fireEvent("click", this, e);
25712     },
25713     
25714     onTriggerPress  : function(e)
25715     {   
25716         if (this.isVisible()) {
25717             this.hide();
25718         } else {
25719             this.show();
25720         }
25721     },
25722     
25723     isVisible : function(){
25724         return !this.hidden;
25725     },
25726     
25727     show : function()
25728     {
25729         this.fireEvent("beforeshow", this);
25730         
25731         this.hidden = false;
25732         this.el.addClass('open');
25733         
25734         Roo.get(document).on("mouseup", this.onMouseUp, this);
25735         
25736         this.fireEvent("show", this);
25737         
25738         
25739     },
25740     
25741     hide : function()
25742     {
25743         this.fireEvent("beforehide", this);
25744         
25745         this.hidden = true;
25746         this.el.removeClass('open');
25747         
25748         Roo.get(document).un("mouseup", this.onMouseUp);
25749         
25750         this.fireEvent("hide", this);
25751     },
25752     
25753     onMouseUp : function()
25754     {
25755         this.hide();
25756     }
25757     
25758 });
25759
25760  
25761  /*
25762  * - LGPL
25763  *
25764  * menu item
25765  * 
25766  */
25767 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25768
25769 /**
25770  * @class Roo.bootstrap.menu.Item
25771  * @extends Roo.bootstrap.Component
25772  * Bootstrap MenuItem class
25773  * @cfg {Boolean} submenu (true | false) default false
25774  * @cfg {String} html text of the item
25775  * @cfg {String} href the link
25776  * @cfg {Boolean} disable (true | false) default false
25777  * @cfg {Boolean} preventDefault (true | false) default true
25778  * @cfg {String} icon Font awesome icon
25779  * @cfg {String} pos Submenu align to (left | right) default right 
25780  * 
25781  * 
25782  * @constructor
25783  * Create a new Item
25784  * @param {Object} config The config object
25785  */
25786
25787
25788 Roo.bootstrap.menu.Item = function(config){
25789     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25790     this.addEvents({
25791         /**
25792          * @event mouseover
25793          * Fires when the mouse is hovering over this menu
25794          * @param {Roo.bootstrap.menu.Item} this
25795          * @param {Roo.EventObject} e
25796          */
25797         mouseover : true,
25798         /**
25799          * @event mouseout
25800          * Fires when the mouse exits this menu
25801          * @param {Roo.bootstrap.menu.Item} this
25802          * @param {Roo.EventObject} e
25803          */
25804         mouseout : true,
25805         // raw events
25806         /**
25807          * @event click
25808          * The raw click event for the entire grid.
25809          * @param {Roo.EventObject} e
25810          */
25811         click : true
25812     });
25813 };
25814
25815 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25816     
25817     submenu : false,
25818     href : '',
25819     html : '',
25820     preventDefault: true,
25821     disable : false,
25822     icon : false,
25823     pos : 'right',
25824     
25825     getAutoCreate : function()
25826     {
25827         var text = [
25828             {
25829                 tag : 'span',
25830                 cls : 'roo-menu-item-text',
25831                 html : this.html
25832             }
25833         ];
25834         
25835         if(this.icon){
25836             text.unshift({
25837                 tag : 'i',
25838                 cls : 'fa ' + this.icon
25839             })
25840         }
25841         
25842         var cfg = {
25843             tag : 'li',
25844             cn : [
25845                 {
25846                     tag : 'a',
25847                     href : this.href || '#',
25848                     cn : text
25849                 }
25850             ]
25851         };
25852         
25853         if(this.disable){
25854             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25855         }
25856         
25857         if(this.submenu){
25858             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25859             
25860             if(this.pos == 'left'){
25861                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25862             }
25863         }
25864         
25865         return cfg;
25866     },
25867     
25868     initEvents : function() 
25869     {
25870         this.el.on('mouseover', this.onMouseOver, this);
25871         this.el.on('mouseout', this.onMouseOut, this);
25872         
25873         this.el.select('a', true).first().on('click', this.onClick, this);
25874         
25875     },
25876     
25877     onClick : function(e)
25878     {
25879         if(this.preventDefault){
25880             e.preventDefault();
25881         }
25882         
25883         this.fireEvent("click", this, e);
25884     },
25885     
25886     onMouseOver : function(e)
25887     {
25888         if(this.submenu && this.pos == 'left'){
25889             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25890         }
25891         
25892         this.fireEvent("mouseover", this, e);
25893     },
25894     
25895     onMouseOut : function(e)
25896     {
25897         this.fireEvent("mouseout", this, e);
25898     }
25899 });
25900
25901  
25902
25903  /*
25904  * - LGPL
25905  *
25906  * menu separator
25907  * 
25908  */
25909 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25910
25911 /**
25912  * @class Roo.bootstrap.menu.Separator
25913  * @extends Roo.bootstrap.Component
25914  * Bootstrap Separator class
25915  * 
25916  * @constructor
25917  * Create a new Separator
25918  * @param {Object} config The config object
25919  */
25920
25921
25922 Roo.bootstrap.menu.Separator = function(config){
25923     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25924 };
25925
25926 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25927     
25928     getAutoCreate : function(){
25929         var cfg = {
25930             tag : 'li',
25931             cls: 'divider'
25932         };
25933         
25934         return cfg;
25935     }
25936    
25937 });
25938
25939  
25940
25941  /*
25942  * - LGPL
25943  *
25944  * Tooltip
25945  * 
25946  */
25947
25948 /**
25949  * @class Roo.bootstrap.Tooltip
25950  * Bootstrap Tooltip class
25951  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25952  * to determine which dom element triggers the tooltip.
25953  * 
25954  * It needs to add support for additional attributes like tooltip-position
25955  * 
25956  * @constructor
25957  * Create a new Toolti
25958  * @param {Object} config The config object
25959  */
25960
25961 Roo.bootstrap.Tooltip = function(config){
25962     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25963     
25964     this.alignment = Roo.bootstrap.Tooltip.alignment;
25965     
25966     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25967         this.alignment = config.alignment;
25968     }
25969     
25970 };
25971
25972 Roo.apply(Roo.bootstrap.Tooltip, {
25973     /**
25974      * @function init initialize tooltip monitoring.
25975      * @static
25976      */
25977     currentEl : false,
25978     currentTip : false,
25979     currentRegion : false,
25980     
25981     //  init : delay?
25982     
25983     init : function()
25984     {
25985         Roo.get(document).on('mouseover', this.enter ,this);
25986         Roo.get(document).on('mouseout', this.leave, this);
25987          
25988         
25989         this.currentTip = new Roo.bootstrap.Tooltip();
25990     },
25991     
25992     enter : function(ev)
25993     {
25994         var dom = ev.getTarget();
25995         
25996         //Roo.log(['enter',dom]);
25997         var el = Roo.fly(dom);
25998         if (this.currentEl) {
25999             //Roo.log(dom);
26000             //Roo.log(this.currentEl);
26001             //Roo.log(this.currentEl.contains(dom));
26002             if (this.currentEl == el) {
26003                 return;
26004             }
26005             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26006                 return;
26007             }
26008
26009         }
26010         
26011         if (this.currentTip.el) {
26012             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26013         }    
26014         //Roo.log(ev);
26015         
26016         if(!el || el.dom == document){
26017             return;
26018         }
26019         
26020         var bindEl = el;
26021         
26022         // you can not look for children, as if el is the body.. then everythign is the child..
26023         if (!el.attr('tooltip')) { //
26024             if (!el.select("[tooltip]").elements.length) {
26025                 return;
26026             }
26027             // is the mouse over this child...?
26028             bindEl = el.select("[tooltip]").first();
26029             var xy = ev.getXY();
26030             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26031                 //Roo.log("not in region.");
26032                 return;
26033             }
26034             //Roo.log("child element over..");
26035             
26036         }
26037         this.currentEl = bindEl;
26038         this.currentTip.bind(bindEl);
26039         this.currentRegion = Roo.lib.Region.getRegion(dom);
26040         this.currentTip.enter();
26041         
26042     },
26043     leave : function(ev)
26044     {
26045         var dom = ev.getTarget();
26046         //Roo.log(['leave',dom]);
26047         if (!this.currentEl) {
26048             return;
26049         }
26050         
26051         
26052         if (dom != this.currentEl.dom) {
26053             return;
26054         }
26055         var xy = ev.getXY();
26056         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26057             return;
26058         }
26059         // only activate leave if mouse cursor is outside... bounding box..
26060         
26061         
26062         
26063         
26064         if (this.currentTip) {
26065             this.currentTip.leave();
26066         }
26067         //Roo.log('clear currentEl');
26068         this.currentEl = false;
26069         
26070         
26071     },
26072     alignment : {
26073         'left' : ['r-l', [-2,0], 'right'],
26074         'right' : ['l-r', [2,0], 'left'],
26075         'bottom' : ['t-b', [0,2], 'top'],
26076         'top' : [ 'b-t', [0,-2], 'bottom']
26077     }
26078     
26079 });
26080
26081
26082 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26083     
26084     
26085     bindEl : false,
26086     
26087     delay : null, // can be { show : 300 , hide: 500}
26088     
26089     timeout : null,
26090     
26091     hoverState : null, //???
26092     
26093     placement : 'bottom', 
26094     
26095     alignment : false,
26096     
26097     getAutoCreate : function(){
26098     
26099         var cfg = {
26100            cls : 'tooltip',
26101            role : 'tooltip',
26102            cn : [
26103                 {
26104                     cls : 'tooltip-arrow'
26105                 },
26106                 {
26107                     cls : 'tooltip-inner'
26108                 }
26109            ]
26110         };
26111         
26112         return cfg;
26113     },
26114     bind : function(el)
26115     {
26116         this.bindEl = el;
26117     },
26118       
26119     
26120     enter : function () {
26121        
26122         if (this.timeout != null) {
26123             clearTimeout(this.timeout);
26124         }
26125         
26126         this.hoverState = 'in';
26127          //Roo.log("enter - show");
26128         if (!this.delay || !this.delay.show) {
26129             this.show();
26130             return;
26131         }
26132         var _t = this;
26133         this.timeout = setTimeout(function () {
26134             if (_t.hoverState == 'in') {
26135                 _t.show();
26136             }
26137         }, this.delay.show);
26138     },
26139     leave : function()
26140     {
26141         clearTimeout(this.timeout);
26142     
26143         this.hoverState = 'out';
26144          if (!this.delay || !this.delay.hide) {
26145             this.hide();
26146             return;
26147         }
26148        
26149         var _t = this;
26150         this.timeout = setTimeout(function () {
26151             //Roo.log("leave - timeout");
26152             
26153             if (_t.hoverState == 'out') {
26154                 _t.hide();
26155                 Roo.bootstrap.Tooltip.currentEl = false;
26156             }
26157         }, delay);
26158     },
26159     
26160     show : function (msg)
26161     {
26162         if (!this.el) {
26163             this.render(document.body);
26164         }
26165         // set content.
26166         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26167         
26168         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26169         
26170         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26171         
26172         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26173         
26174         var placement = typeof this.placement == 'function' ?
26175             this.placement.call(this, this.el, on_el) :
26176             this.placement;
26177             
26178         var autoToken = /\s?auto?\s?/i;
26179         var autoPlace = autoToken.test(placement);
26180         if (autoPlace) {
26181             placement = placement.replace(autoToken, '') || 'top';
26182         }
26183         
26184         //this.el.detach()
26185         //this.el.setXY([0,0]);
26186         this.el.show();
26187         //this.el.dom.style.display='block';
26188         
26189         //this.el.appendTo(on_el);
26190         
26191         var p = this.getPosition();
26192         var box = this.el.getBox();
26193         
26194         if (autoPlace) {
26195             // fixme..
26196         }
26197         
26198         var align = this.alignment[placement];
26199         
26200         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26201         
26202         if(placement == 'top' || placement == 'bottom'){
26203             if(xy[0] < 0){
26204                 placement = 'right';
26205             }
26206             
26207             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26208                 placement = 'left';
26209             }
26210             
26211             var scroll = Roo.select('body', true).first().getScroll();
26212             
26213             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26214                 placement = 'top';
26215             }
26216             
26217             align = this.alignment[placement];
26218         }
26219         
26220         this.el.alignTo(this.bindEl, align[0],align[1]);
26221         //var arrow = this.el.select('.arrow',true).first();
26222         //arrow.set(align[2], 
26223         
26224         this.el.addClass(placement);
26225         
26226         this.el.addClass('in fade');
26227         
26228         this.hoverState = null;
26229         
26230         if (this.el.hasClass('fade')) {
26231             // fade it?
26232         }
26233         
26234     },
26235     hide : function()
26236     {
26237          
26238         if (!this.el) {
26239             return;
26240         }
26241         //this.el.setXY([0,0]);
26242         this.el.removeClass('in');
26243         //this.el.hide();
26244         
26245     }
26246     
26247 });
26248  
26249
26250  /*
26251  * - LGPL
26252  *
26253  * Location Picker
26254  * 
26255  */
26256
26257 /**
26258  * @class Roo.bootstrap.LocationPicker
26259  * @extends Roo.bootstrap.Component
26260  * Bootstrap LocationPicker class
26261  * @cfg {Number} latitude Position when init default 0
26262  * @cfg {Number} longitude Position when init default 0
26263  * @cfg {Number} zoom default 15
26264  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26265  * @cfg {Boolean} mapTypeControl default false
26266  * @cfg {Boolean} disableDoubleClickZoom default false
26267  * @cfg {Boolean} scrollwheel default true
26268  * @cfg {Boolean} streetViewControl default false
26269  * @cfg {Number} radius default 0
26270  * @cfg {String} locationName
26271  * @cfg {Boolean} draggable default true
26272  * @cfg {Boolean} enableAutocomplete default false
26273  * @cfg {Boolean} enableReverseGeocode default true
26274  * @cfg {String} markerTitle
26275  * 
26276  * @constructor
26277  * Create a new LocationPicker
26278  * @param {Object} config The config object
26279  */
26280
26281
26282 Roo.bootstrap.LocationPicker = function(config){
26283     
26284     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26285     
26286     this.addEvents({
26287         /**
26288          * @event initial
26289          * Fires when the picker initialized.
26290          * @param {Roo.bootstrap.LocationPicker} this
26291          * @param {Google Location} location
26292          */
26293         initial : true,
26294         /**
26295          * @event positionchanged
26296          * Fires when the picker position changed.
26297          * @param {Roo.bootstrap.LocationPicker} this
26298          * @param {Google Location} location
26299          */
26300         positionchanged : true,
26301         /**
26302          * @event resize
26303          * Fires when the map resize.
26304          * @param {Roo.bootstrap.LocationPicker} this
26305          */
26306         resize : true,
26307         /**
26308          * @event show
26309          * Fires when the map show.
26310          * @param {Roo.bootstrap.LocationPicker} this
26311          */
26312         show : true,
26313         /**
26314          * @event hide
26315          * Fires when the map hide.
26316          * @param {Roo.bootstrap.LocationPicker} this
26317          */
26318         hide : true,
26319         /**
26320          * @event mapClick
26321          * Fires when click the map.
26322          * @param {Roo.bootstrap.LocationPicker} this
26323          * @param {Map event} e
26324          */
26325         mapClick : true,
26326         /**
26327          * @event mapRightClick
26328          * Fires when right click the map.
26329          * @param {Roo.bootstrap.LocationPicker} this
26330          * @param {Map event} e
26331          */
26332         mapRightClick : true,
26333         /**
26334          * @event markerClick
26335          * Fires when click the marker.
26336          * @param {Roo.bootstrap.LocationPicker} this
26337          * @param {Map event} e
26338          */
26339         markerClick : true,
26340         /**
26341          * @event markerRightClick
26342          * Fires when right click the marker.
26343          * @param {Roo.bootstrap.LocationPicker} this
26344          * @param {Map event} e
26345          */
26346         markerRightClick : true,
26347         /**
26348          * @event OverlayViewDraw
26349          * Fires when OverlayView Draw
26350          * @param {Roo.bootstrap.LocationPicker} this
26351          */
26352         OverlayViewDraw : true,
26353         /**
26354          * @event OverlayViewOnAdd
26355          * Fires when OverlayView Draw
26356          * @param {Roo.bootstrap.LocationPicker} this
26357          */
26358         OverlayViewOnAdd : true,
26359         /**
26360          * @event OverlayViewOnRemove
26361          * Fires when OverlayView Draw
26362          * @param {Roo.bootstrap.LocationPicker} this
26363          */
26364         OverlayViewOnRemove : true,
26365         /**
26366          * @event OverlayViewShow
26367          * Fires when OverlayView Draw
26368          * @param {Roo.bootstrap.LocationPicker} this
26369          * @param {Pixel} cpx
26370          */
26371         OverlayViewShow : true,
26372         /**
26373          * @event OverlayViewHide
26374          * Fires when OverlayView Draw
26375          * @param {Roo.bootstrap.LocationPicker} this
26376          */
26377         OverlayViewHide : true,
26378         /**
26379          * @event loadexception
26380          * Fires when load google lib failed.
26381          * @param {Roo.bootstrap.LocationPicker} this
26382          */
26383         loadexception : true
26384     });
26385         
26386 };
26387
26388 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26389     
26390     gMapContext: false,
26391     
26392     latitude: 0,
26393     longitude: 0,
26394     zoom: 15,
26395     mapTypeId: false,
26396     mapTypeControl: false,
26397     disableDoubleClickZoom: false,
26398     scrollwheel: true,
26399     streetViewControl: false,
26400     radius: 0,
26401     locationName: '',
26402     draggable: true,
26403     enableAutocomplete: false,
26404     enableReverseGeocode: true,
26405     markerTitle: '',
26406     
26407     getAutoCreate: function()
26408     {
26409
26410         var cfg = {
26411             tag: 'div',
26412             cls: 'roo-location-picker'
26413         };
26414         
26415         return cfg
26416     },
26417     
26418     initEvents: function(ct, position)
26419     {       
26420         if(!this.el.getWidth() || this.isApplied()){
26421             return;
26422         }
26423         
26424         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26425         
26426         this.initial();
26427     },
26428     
26429     initial: function()
26430     {
26431         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26432             this.fireEvent('loadexception', this);
26433             return;
26434         }
26435         
26436         if(!this.mapTypeId){
26437             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26438         }
26439         
26440         this.gMapContext = this.GMapContext();
26441         
26442         this.initOverlayView();
26443         
26444         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26445         
26446         var _this = this;
26447                 
26448         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26449             _this.setPosition(_this.gMapContext.marker.position);
26450         });
26451         
26452         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26453             _this.fireEvent('mapClick', this, event);
26454             
26455         });
26456
26457         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26458             _this.fireEvent('mapRightClick', this, event);
26459             
26460         });
26461         
26462         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26463             _this.fireEvent('markerClick', this, event);
26464             
26465         });
26466
26467         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26468             _this.fireEvent('markerRightClick', this, event);
26469             
26470         });
26471         
26472         this.setPosition(this.gMapContext.location);
26473         
26474         this.fireEvent('initial', this, this.gMapContext.location);
26475     },
26476     
26477     initOverlayView: function()
26478     {
26479         var _this = this;
26480         
26481         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26482             
26483             draw: function()
26484             {
26485                 _this.fireEvent('OverlayViewDraw', _this);
26486             },
26487             
26488             onAdd: function()
26489             {
26490                 _this.fireEvent('OverlayViewOnAdd', _this);
26491             },
26492             
26493             onRemove: function()
26494             {
26495                 _this.fireEvent('OverlayViewOnRemove', _this);
26496             },
26497             
26498             show: function(cpx)
26499             {
26500                 _this.fireEvent('OverlayViewShow', _this, cpx);
26501             },
26502             
26503             hide: function()
26504             {
26505                 _this.fireEvent('OverlayViewHide', _this);
26506             }
26507             
26508         });
26509     },
26510     
26511     fromLatLngToContainerPixel: function(event)
26512     {
26513         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26514     },
26515     
26516     isApplied: function() 
26517     {
26518         return this.getGmapContext() == false ? false : true;
26519     },
26520     
26521     getGmapContext: function() 
26522     {
26523         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26524     },
26525     
26526     GMapContext: function() 
26527     {
26528         var position = new google.maps.LatLng(this.latitude, this.longitude);
26529         
26530         var _map = new google.maps.Map(this.el.dom, {
26531             center: position,
26532             zoom: this.zoom,
26533             mapTypeId: this.mapTypeId,
26534             mapTypeControl: this.mapTypeControl,
26535             disableDoubleClickZoom: this.disableDoubleClickZoom,
26536             scrollwheel: this.scrollwheel,
26537             streetViewControl: this.streetViewControl,
26538             locationName: this.locationName,
26539             draggable: this.draggable,
26540             enableAutocomplete: this.enableAutocomplete,
26541             enableReverseGeocode: this.enableReverseGeocode
26542         });
26543         
26544         var _marker = new google.maps.Marker({
26545             position: position,
26546             map: _map,
26547             title: this.markerTitle,
26548             draggable: this.draggable
26549         });
26550         
26551         return {
26552             map: _map,
26553             marker: _marker,
26554             circle: null,
26555             location: position,
26556             radius: this.radius,
26557             locationName: this.locationName,
26558             addressComponents: {
26559                 formatted_address: null,
26560                 addressLine1: null,
26561                 addressLine2: null,
26562                 streetName: null,
26563                 streetNumber: null,
26564                 city: null,
26565                 district: null,
26566                 state: null,
26567                 stateOrProvince: null
26568             },
26569             settings: this,
26570             domContainer: this.el.dom,
26571             geodecoder: new google.maps.Geocoder()
26572         };
26573     },
26574     
26575     drawCircle: function(center, radius, options) 
26576     {
26577         if (this.gMapContext.circle != null) {
26578             this.gMapContext.circle.setMap(null);
26579         }
26580         if (radius > 0) {
26581             radius *= 1;
26582             options = Roo.apply({}, options, {
26583                 strokeColor: "#0000FF",
26584                 strokeOpacity: .35,
26585                 strokeWeight: 2,
26586                 fillColor: "#0000FF",
26587                 fillOpacity: .2
26588             });
26589             
26590             options.map = this.gMapContext.map;
26591             options.radius = radius;
26592             options.center = center;
26593             this.gMapContext.circle = new google.maps.Circle(options);
26594             return this.gMapContext.circle;
26595         }
26596         
26597         return null;
26598     },
26599     
26600     setPosition: function(location) 
26601     {
26602         this.gMapContext.location = location;
26603         this.gMapContext.marker.setPosition(location);
26604         this.gMapContext.map.panTo(location);
26605         this.drawCircle(location, this.gMapContext.radius, {});
26606         
26607         var _this = this;
26608         
26609         if (this.gMapContext.settings.enableReverseGeocode) {
26610             this.gMapContext.geodecoder.geocode({
26611                 latLng: this.gMapContext.location
26612             }, function(results, status) {
26613                 
26614                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26615                     _this.gMapContext.locationName = results[0].formatted_address;
26616                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26617                     
26618                     _this.fireEvent('positionchanged', this, location);
26619                 }
26620             });
26621             
26622             return;
26623         }
26624         
26625         this.fireEvent('positionchanged', this, location);
26626     },
26627     
26628     resize: function()
26629     {
26630         google.maps.event.trigger(this.gMapContext.map, "resize");
26631         
26632         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26633         
26634         this.fireEvent('resize', this);
26635     },
26636     
26637     setPositionByLatLng: function(latitude, longitude)
26638     {
26639         this.setPosition(new google.maps.LatLng(latitude, longitude));
26640     },
26641     
26642     getCurrentPosition: function() 
26643     {
26644         return {
26645             latitude: this.gMapContext.location.lat(),
26646             longitude: this.gMapContext.location.lng()
26647         };
26648     },
26649     
26650     getAddressName: function() 
26651     {
26652         return this.gMapContext.locationName;
26653     },
26654     
26655     getAddressComponents: function() 
26656     {
26657         return this.gMapContext.addressComponents;
26658     },
26659     
26660     address_component_from_google_geocode: function(address_components) 
26661     {
26662         var result = {};
26663         
26664         for (var i = 0; i < address_components.length; i++) {
26665             var component = address_components[i];
26666             if (component.types.indexOf("postal_code") >= 0) {
26667                 result.postalCode = component.short_name;
26668             } else if (component.types.indexOf("street_number") >= 0) {
26669                 result.streetNumber = component.short_name;
26670             } else if (component.types.indexOf("route") >= 0) {
26671                 result.streetName = component.short_name;
26672             } else if (component.types.indexOf("neighborhood") >= 0) {
26673                 result.city = component.short_name;
26674             } else if (component.types.indexOf("locality") >= 0) {
26675                 result.city = component.short_name;
26676             } else if (component.types.indexOf("sublocality") >= 0) {
26677                 result.district = component.short_name;
26678             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26679                 result.stateOrProvince = component.short_name;
26680             } else if (component.types.indexOf("country") >= 0) {
26681                 result.country = component.short_name;
26682             }
26683         }
26684         
26685         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26686         result.addressLine2 = "";
26687         return result;
26688     },
26689     
26690     setZoomLevel: function(zoom)
26691     {
26692         this.gMapContext.map.setZoom(zoom);
26693     },
26694     
26695     show: function()
26696     {
26697         if(!this.el){
26698             return;
26699         }
26700         
26701         this.el.show();
26702         
26703         this.resize();
26704         
26705         this.fireEvent('show', this);
26706     },
26707     
26708     hide: function()
26709     {
26710         if(!this.el){
26711             return;
26712         }
26713         
26714         this.el.hide();
26715         
26716         this.fireEvent('hide', this);
26717     }
26718     
26719 });
26720
26721 Roo.apply(Roo.bootstrap.LocationPicker, {
26722     
26723     OverlayView : function(map, options)
26724     {
26725         options = options || {};
26726         
26727         this.setMap(map);
26728     }
26729     
26730     
26731 });/*
26732  * - LGPL
26733  *
26734  * Alert
26735  * 
26736  */
26737
26738 /**
26739  * @class Roo.bootstrap.Alert
26740  * @extends Roo.bootstrap.Component
26741  * Bootstrap Alert class
26742  * @cfg {String} title The title of alert
26743  * @cfg {String} html The content of alert
26744  * @cfg {String} weight (  success | info | warning | danger )
26745  * @cfg {String} faicon font-awesomeicon
26746  * 
26747  * @constructor
26748  * Create a new alert
26749  * @param {Object} config The config object
26750  */
26751
26752
26753 Roo.bootstrap.Alert = function(config){
26754     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26755     
26756 };
26757
26758 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26759     
26760     title: '',
26761     html: '',
26762     weight: false,
26763     faicon: false,
26764     
26765     getAutoCreate : function()
26766     {
26767         
26768         var cfg = {
26769             tag : 'div',
26770             cls : 'alert',
26771             cn : [
26772                 {
26773                     tag : 'i',
26774                     cls : 'roo-alert-icon'
26775                     
26776                 },
26777                 {
26778                     tag : 'b',
26779                     cls : 'roo-alert-title',
26780                     html : this.title
26781                 },
26782                 {
26783                     tag : 'span',
26784                     cls : 'roo-alert-text',
26785                     html : this.html
26786                 }
26787             ]
26788         };
26789         
26790         if(this.faicon){
26791             cfg.cn[0].cls += ' fa ' + this.faicon;
26792         }
26793         
26794         if(this.weight){
26795             cfg.cls += ' alert-' + this.weight;
26796         }
26797         
26798         return cfg;
26799     },
26800     
26801     initEvents: function() 
26802     {
26803         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26804     },
26805     
26806     setTitle : function(str)
26807     {
26808         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26809     },
26810     
26811     setText : function(str)
26812     {
26813         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26814     },
26815     
26816     setWeight : function(weight)
26817     {
26818         if(this.weight){
26819             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26820         }
26821         
26822         this.weight = weight;
26823         
26824         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26825     },
26826     
26827     setIcon : function(icon)
26828     {
26829         if(this.faicon){
26830             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26831         }
26832         
26833         this.faicon = icon;
26834         
26835         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26836     },
26837     
26838     hide: function() 
26839     {
26840         this.el.hide();   
26841     },
26842     
26843     show: function() 
26844     {  
26845         this.el.show();   
26846     }
26847     
26848 });
26849
26850  
26851 /*
26852 * Licence: LGPL
26853 */
26854
26855 /**
26856  * @class Roo.bootstrap.UploadCropbox
26857  * @extends Roo.bootstrap.Component
26858  * Bootstrap UploadCropbox class
26859  * @cfg {String} emptyText show when image has been loaded
26860  * @cfg {String} rotateNotify show when image too small to rotate
26861  * @cfg {Number} errorTimeout default 3000
26862  * @cfg {Number} minWidth default 300
26863  * @cfg {Number} minHeight default 300
26864  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26865  * @cfg {Boolean} isDocument (true|false) default false
26866  * @cfg {String} url action url
26867  * @cfg {String} paramName default 'imageUpload'
26868  * @cfg {String} method default POST
26869  * @cfg {Boolean} loadMask (true|false) default true
26870  * @cfg {Boolean} loadingText default 'Loading...'
26871  * 
26872  * @constructor
26873  * Create a new UploadCropbox
26874  * @param {Object} config The config object
26875  */
26876
26877 Roo.bootstrap.UploadCropbox = function(config){
26878     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26879     
26880     this.addEvents({
26881         /**
26882          * @event beforeselectfile
26883          * Fire before select file
26884          * @param {Roo.bootstrap.UploadCropbox} this
26885          */
26886         "beforeselectfile" : true,
26887         /**
26888          * @event initial
26889          * Fire after initEvent
26890          * @param {Roo.bootstrap.UploadCropbox} this
26891          */
26892         "initial" : true,
26893         /**
26894          * @event crop
26895          * Fire after initEvent
26896          * @param {Roo.bootstrap.UploadCropbox} this
26897          * @param {String} data
26898          */
26899         "crop" : true,
26900         /**
26901          * @event prepare
26902          * Fire when preparing the file data
26903          * @param {Roo.bootstrap.UploadCropbox} this
26904          * @param {Object} file
26905          */
26906         "prepare" : true,
26907         /**
26908          * @event exception
26909          * Fire when get exception
26910          * @param {Roo.bootstrap.UploadCropbox} this
26911          * @param {XMLHttpRequest} xhr
26912          */
26913         "exception" : true,
26914         /**
26915          * @event beforeloadcanvas
26916          * Fire before load the canvas
26917          * @param {Roo.bootstrap.UploadCropbox} this
26918          * @param {String} src
26919          */
26920         "beforeloadcanvas" : true,
26921         /**
26922          * @event trash
26923          * Fire when trash image
26924          * @param {Roo.bootstrap.UploadCropbox} this
26925          */
26926         "trash" : true,
26927         /**
26928          * @event download
26929          * Fire when download the image
26930          * @param {Roo.bootstrap.UploadCropbox} this
26931          */
26932         "download" : true,
26933         /**
26934          * @event footerbuttonclick
26935          * Fire when footerbuttonclick
26936          * @param {Roo.bootstrap.UploadCropbox} this
26937          * @param {String} type
26938          */
26939         "footerbuttonclick" : true,
26940         /**
26941          * @event resize
26942          * Fire when resize
26943          * @param {Roo.bootstrap.UploadCropbox} this
26944          */
26945         "resize" : true,
26946         /**
26947          * @event rotate
26948          * Fire when rotate the image
26949          * @param {Roo.bootstrap.UploadCropbox} this
26950          * @param {String} pos
26951          */
26952         "rotate" : true,
26953         /**
26954          * @event inspect
26955          * Fire when inspect the file
26956          * @param {Roo.bootstrap.UploadCropbox} this
26957          * @param {Object} file
26958          */
26959         "inspect" : true,
26960         /**
26961          * @event upload
26962          * Fire when xhr upload the file
26963          * @param {Roo.bootstrap.UploadCropbox} this
26964          * @param {Object} data
26965          */
26966         "upload" : true,
26967         /**
26968          * @event arrange
26969          * Fire when arrange the file data
26970          * @param {Roo.bootstrap.UploadCropbox} this
26971          * @param {Object} formData
26972          */
26973         "arrange" : true
26974     });
26975     
26976     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26977 };
26978
26979 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26980     
26981     emptyText : 'Click to upload image',
26982     rotateNotify : 'Image is too small to rotate',
26983     errorTimeout : 3000,
26984     scale : 0,
26985     baseScale : 1,
26986     rotate : 0,
26987     dragable : false,
26988     pinching : false,
26989     mouseX : 0,
26990     mouseY : 0,
26991     cropData : false,
26992     minWidth : 300,
26993     minHeight : 300,
26994     file : false,
26995     exif : {},
26996     baseRotate : 1,
26997     cropType : 'image/jpeg',
26998     buttons : false,
26999     canvasLoaded : false,
27000     isDocument : false,
27001     method : 'POST',
27002     paramName : 'imageUpload',
27003     loadMask : true,
27004     loadingText : 'Loading...',
27005     maskEl : false,
27006     
27007     getAutoCreate : function()
27008     {
27009         var cfg = {
27010             tag : 'div',
27011             cls : 'roo-upload-cropbox',
27012             cn : [
27013                 {
27014                     tag : 'input',
27015                     cls : 'roo-upload-cropbox-selector',
27016                     type : 'file'
27017                 },
27018                 {
27019                     tag : 'div',
27020                     cls : 'roo-upload-cropbox-body',
27021                     style : 'cursor:pointer',
27022                     cn : [
27023                         {
27024                             tag : 'div',
27025                             cls : 'roo-upload-cropbox-preview'
27026                         },
27027                         {
27028                             tag : 'div',
27029                             cls : 'roo-upload-cropbox-thumb'
27030                         },
27031                         {
27032                             tag : 'div',
27033                             cls : 'roo-upload-cropbox-empty-notify',
27034                             html : this.emptyText
27035                         },
27036                         {
27037                             tag : 'div',
27038                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27039                             html : this.rotateNotify
27040                         }
27041                     ]
27042                 },
27043                 {
27044                     tag : 'div',
27045                     cls : 'roo-upload-cropbox-footer',
27046                     cn : {
27047                         tag : 'div',
27048                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27049                         cn : []
27050                     }
27051                 }
27052             ]
27053         };
27054         
27055         return cfg;
27056     },
27057     
27058     onRender : function(ct, position)
27059     {
27060         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27061         
27062         if (this.buttons.length) {
27063             
27064             Roo.each(this.buttons, function(bb) {
27065                 
27066                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27067                 
27068                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27069                 
27070             }, this);
27071         }
27072         
27073         if(this.loadMask){
27074             this.maskEl = this.el;
27075         }
27076     },
27077     
27078     initEvents : function()
27079     {
27080         this.urlAPI = (window.createObjectURL && window) || 
27081                                 (window.URL && URL.revokeObjectURL && URL) || 
27082                                 (window.webkitURL && webkitURL);
27083                         
27084         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27085         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27086         
27087         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27088         this.selectorEl.hide();
27089         
27090         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27091         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27092         
27093         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27094         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27095         this.thumbEl.hide();
27096         
27097         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27098         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27099         
27100         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27101         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27102         this.errorEl.hide();
27103         
27104         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27105         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27106         this.footerEl.hide();
27107         
27108         this.setThumbBoxSize();
27109         
27110         this.bind();
27111         
27112         this.resize();
27113         
27114         this.fireEvent('initial', this);
27115     },
27116
27117     bind : function()
27118     {
27119         var _this = this;
27120         
27121         window.addEventListener("resize", function() { _this.resize(); } );
27122         
27123         this.bodyEl.on('click', this.beforeSelectFile, this);
27124         
27125         if(Roo.isTouch){
27126             this.bodyEl.on('touchstart', this.onTouchStart, this);
27127             this.bodyEl.on('touchmove', this.onTouchMove, this);
27128             this.bodyEl.on('touchend', this.onTouchEnd, this);
27129         }
27130         
27131         if(!Roo.isTouch){
27132             this.bodyEl.on('mousedown', this.onMouseDown, this);
27133             this.bodyEl.on('mousemove', this.onMouseMove, this);
27134             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27135             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27136             Roo.get(document).on('mouseup', this.onMouseUp, this);
27137         }
27138         
27139         this.selectorEl.on('change', this.onFileSelected, this);
27140     },
27141     
27142     reset : function()
27143     {    
27144         this.scale = 0;
27145         this.baseScale = 1;
27146         this.rotate = 0;
27147         this.baseRotate = 1;
27148         this.dragable = false;
27149         this.pinching = false;
27150         this.mouseX = 0;
27151         this.mouseY = 0;
27152         this.cropData = false;
27153         this.notifyEl.dom.innerHTML = this.emptyText;
27154         
27155         this.selectorEl.dom.value = '';
27156         
27157     },
27158     
27159     resize : function()
27160     {
27161         if(this.fireEvent('resize', this) != false){
27162             this.setThumbBoxPosition();
27163             this.setCanvasPosition();
27164         }
27165     },
27166     
27167     onFooterButtonClick : function(e, el, o, type)
27168     {
27169         switch (type) {
27170             case 'rotate-left' :
27171                 this.onRotateLeft(e);
27172                 break;
27173             case 'rotate-right' :
27174                 this.onRotateRight(e);
27175                 break;
27176             case 'picture' :
27177                 this.beforeSelectFile(e);
27178                 break;
27179             case 'trash' :
27180                 this.trash(e);
27181                 break;
27182             case 'crop' :
27183                 this.crop(e);
27184                 break;
27185             case 'download' :
27186                 this.download(e);
27187                 break;
27188             default :
27189                 break;
27190         }
27191         
27192         this.fireEvent('footerbuttonclick', this, type);
27193     },
27194     
27195     beforeSelectFile : function(e)
27196     {
27197         e.preventDefault();
27198         
27199         if(this.fireEvent('beforeselectfile', this) != false){
27200             this.selectorEl.dom.click();
27201         }
27202     },
27203     
27204     onFileSelected : function(e)
27205     {
27206         e.preventDefault();
27207         
27208         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27209             return;
27210         }
27211         
27212         var file = this.selectorEl.dom.files[0];
27213         
27214         if(this.fireEvent('inspect', this, file) != false){
27215             this.prepare(file);
27216         }
27217         
27218     },
27219     
27220     trash : function(e)
27221     {
27222         this.fireEvent('trash', this);
27223     },
27224     
27225     download : function(e)
27226     {
27227         this.fireEvent('download', this);
27228     },
27229     
27230     loadCanvas : function(src)
27231     {   
27232         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27233             
27234             this.reset();
27235             
27236             this.imageEl = document.createElement('img');
27237             
27238             var _this = this;
27239             
27240             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27241             
27242             this.imageEl.src = src;
27243         }
27244     },
27245     
27246     onLoadCanvas : function()
27247     {   
27248         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27249         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27250         
27251         this.bodyEl.un('click', this.beforeSelectFile, this);
27252         
27253         this.notifyEl.hide();
27254         this.thumbEl.show();
27255         this.footerEl.show();
27256         
27257         this.baseRotateLevel();
27258         
27259         if(this.isDocument){
27260             this.setThumbBoxSize();
27261         }
27262         
27263         this.setThumbBoxPosition();
27264         
27265         this.baseScaleLevel();
27266         
27267         this.draw();
27268         
27269         this.resize();
27270         
27271         this.canvasLoaded = true;
27272         
27273         if(this.loadMask){
27274             this.maskEl.unmask();
27275         }
27276         
27277     },
27278     
27279     setCanvasPosition : function()
27280     {   
27281         if(!this.canvasEl){
27282             return;
27283         }
27284         
27285         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27286         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27287         
27288         this.previewEl.setLeft(pw);
27289         this.previewEl.setTop(ph);
27290         
27291     },
27292     
27293     onMouseDown : function(e)
27294     {   
27295         e.stopEvent();
27296         
27297         this.dragable = true;
27298         this.pinching = false;
27299         
27300         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27301             this.dragable = false;
27302             return;
27303         }
27304         
27305         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27306         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27307         
27308     },
27309     
27310     onMouseMove : function(e)
27311     {   
27312         e.stopEvent();
27313         
27314         if(!this.canvasLoaded){
27315             return;
27316         }
27317         
27318         if (!this.dragable){
27319             return;
27320         }
27321         
27322         var minX = Math.ceil(this.thumbEl.getLeft(true));
27323         var minY = Math.ceil(this.thumbEl.getTop(true));
27324         
27325         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27326         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27327         
27328         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27329         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27330         
27331         x = x - this.mouseX;
27332         y = y - this.mouseY;
27333         
27334         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27335         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27336         
27337         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27338         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27339         
27340         this.previewEl.setLeft(bgX);
27341         this.previewEl.setTop(bgY);
27342         
27343         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27344         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27345     },
27346     
27347     onMouseUp : function(e)
27348     {   
27349         e.stopEvent();
27350         
27351         this.dragable = false;
27352     },
27353     
27354     onMouseWheel : function(e)
27355     {   
27356         e.stopEvent();
27357         
27358         this.startScale = this.scale;
27359         
27360         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27361         
27362         if(!this.zoomable()){
27363             this.scale = this.startScale;
27364             return;
27365         }
27366         
27367         this.draw();
27368         
27369         return;
27370     },
27371     
27372     zoomable : function()
27373     {
27374         var minScale = this.thumbEl.getWidth() / this.minWidth;
27375         
27376         if(this.minWidth < this.minHeight){
27377             minScale = this.thumbEl.getHeight() / this.minHeight;
27378         }
27379         
27380         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27381         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27382         
27383         if(
27384                 this.isDocument &&
27385                 (this.rotate == 0 || this.rotate == 180) && 
27386                 (
27387                     width > this.imageEl.OriginWidth || 
27388                     height > this.imageEl.OriginHeight ||
27389                     (width < this.minWidth && height < this.minHeight)
27390                 )
27391         ){
27392             return false;
27393         }
27394         
27395         if(
27396                 this.isDocument &&
27397                 (this.rotate == 90 || this.rotate == 270) && 
27398                 (
27399                     width > this.imageEl.OriginWidth || 
27400                     height > this.imageEl.OriginHeight ||
27401                     (width < this.minHeight && height < this.minWidth)
27402                 )
27403         ){
27404             return false;
27405         }
27406         
27407         if(
27408                 !this.isDocument &&
27409                 (this.rotate == 0 || this.rotate == 180) && 
27410                 (
27411                     width < this.minWidth || 
27412                     width > this.imageEl.OriginWidth || 
27413                     height < this.minHeight || 
27414                     height > this.imageEl.OriginHeight
27415                 )
27416         ){
27417             return false;
27418         }
27419         
27420         if(
27421                 !this.isDocument &&
27422                 (this.rotate == 90 || this.rotate == 270) && 
27423                 (
27424                     width < this.minHeight || 
27425                     width > this.imageEl.OriginWidth || 
27426                     height < this.minWidth || 
27427                     height > this.imageEl.OriginHeight
27428                 )
27429         ){
27430             return false;
27431         }
27432         
27433         return true;
27434         
27435     },
27436     
27437     onRotateLeft : function(e)
27438     {   
27439         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27440             
27441             var minScale = this.thumbEl.getWidth() / this.minWidth;
27442             
27443             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27444             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27445             
27446             this.startScale = this.scale;
27447             
27448             while (this.getScaleLevel() < minScale){
27449             
27450                 this.scale = this.scale + 1;
27451                 
27452                 if(!this.zoomable()){
27453                     break;
27454                 }
27455                 
27456                 if(
27457                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27458                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27459                 ){
27460                     continue;
27461                 }
27462                 
27463                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27464
27465                 this.draw();
27466                 
27467                 return;
27468             }
27469             
27470             this.scale = this.startScale;
27471             
27472             this.onRotateFail();
27473             
27474             return false;
27475         }
27476         
27477         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27478
27479         if(this.isDocument){
27480             this.setThumbBoxSize();
27481             this.setThumbBoxPosition();
27482             this.setCanvasPosition();
27483         }
27484         
27485         this.draw();
27486         
27487         this.fireEvent('rotate', this, 'left');
27488         
27489     },
27490     
27491     onRotateRight : function(e)
27492     {
27493         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27494             
27495             var minScale = this.thumbEl.getWidth() / this.minWidth;
27496         
27497             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27498             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27499             
27500             this.startScale = this.scale;
27501             
27502             while (this.getScaleLevel() < minScale){
27503             
27504                 this.scale = this.scale + 1;
27505                 
27506                 if(!this.zoomable()){
27507                     break;
27508                 }
27509                 
27510                 if(
27511                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27512                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27513                 ){
27514                     continue;
27515                 }
27516                 
27517                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27518
27519                 this.draw();
27520                 
27521                 return;
27522             }
27523             
27524             this.scale = this.startScale;
27525             
27526             this.onRotateFail();
27527             
27528             return false;
27529         }
27530         
27531         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27532
27533         if(this.isDocument){
27534             this.setThumbBoxSize();
27535             this.setThumbBoxPosition();
27536             this.setCanvasPosition();
27537         }
27538         
27539         this.draw();
27540         
27541         this.fireEvent('rotate', this, 'right');
27542     },
27543     
27544     onRotateFail : function()
27545     {
27546         this.errorEl.show(true);
27547         
27548         var _this = this;
27549         
27550         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27551     },
27552     
27553     draw : function()
27554     {
27555         this.previewEl.dom.innerHTML = '';
27556         
27557         var canvasEl = document.createElement("canvas");
27558         
27559         var contextEl = canvasEl.getContext("2d");
27560         
27561         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27562         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27563         var center = this.imageEl.OriginWidth / 2;
27564         
27565         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27566             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27567             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27568             center = this.imageEl.OriginHeight / 2;
27569         }
27570         
27571         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27572         
27573         contextEl.translate(center, center);
27574         contextEl.rotate(this.rotate * Math.PI / 180);
27575
27576         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27577         
27578         this.canvasEl = document.createElement("canvas");
27579         
27580         this.contextEl = this.canvasEl.getContext("2d");
27581         
27582         switch (this.rotate) {
27583             case 0 :
27584                 
27585                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27586                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27587                 
27588                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27589                 
27590                 break;
27591             case 90 : 
27592                 
27593                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27594                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27595                 
27596                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27597                     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);
27598                     break;
27599                 }
27600                 
27601                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27602                 
27603                 break;
27604             case 180 :
27605                 
27606                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27607                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27608                 
27609                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27610                     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);
27611                     break;
27612                 }
27613                 
27614                 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);
27615                 
27616                 break;
27617             case 270 :
27618                 
27619                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27620                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27621         
27622                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27623                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27624                     break;
27625                 }
27626                 
27627                 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);
27628                 
27629                 break;
27630             default : 
27631                 break;
27632         }
27633         
27634         this.previewEl.appendChild(this.canvasEl);
27635         
27636         this.setCanvasPosition();
27637     },
27638     
27639     crop : function()
27640     {
27641         if(!this.canvasLoaded){
27642             return;
27643         }
27644         
27645         var imageCanvas = document.createElement("canvas");
27646         
27647         var imageContext = imageCanvas.getContext("2d");
27648         
27649         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27650         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27651         
27652         var center = imageCanvas.width / 2;
27653         
27654         imageContext.translate(center, center);
27655         
27656         imageContext.rotate(this.rotate * Math.PI / 180);
27657         
27658         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27659         
27660         var canvas = document.createElement("canvas");
27661         
27662         var context = canvas.getContext("2d");
27663                 
27664         canvas.width = this.minWidth;
27665         canvas.height = this.minHeight;
27666
27667         switch (this.rotate) {
27668             case 0 :
27669                 
27670                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27671                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27672                 
27673                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27674                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27675                 
27676                 var targetWidth = this.minWidth - 2 * x;
27677                 var targetHeight = this.minHeight - 2 * y;
27678                 
27679                 var scale = 1;
27680                 
27681                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27682                     scale = targetWidth / width;
27683                 }
27684                 
27685                 if(x > 0 && y == 0){
27686                     scale = targetHeight / height;
27687                 }
27688                 
27689                 if(x > 0 && y > 0){
27690                     scale = targetWidth / width;
27691                     
27692                     if(width < height){
27693                         scale = targetHeight / height;
27694                     }
27695                 }
27696                 
27697                 context.scale(scale, scale);
27698                 
27699                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27700                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27701
27702                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27703                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27704
27705                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27706                 
27707                 break;
27708             case 90 : 
27709                 
27710                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27711                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27712                 
27713                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27714                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27715                 
27716                 var targetWidth = this.minWidth - 2 * x;
27717                 var targetHeight = this.minHeight - 2 * y;
27718                 
27719                 var scale = 1;
27720                 
27721                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27722                     scale = targetWidth / width;
27723                 }
27724                 
27725                 if(x > 0 && y == 0){
27726                     scale = targetHeight / height;
27727                 }
27728                 
27729                 if(x > 0 && y > 0){
27730                     scale = targetWidth / width;
27731                     
27732                     if(width < height){
27733                         scale = targetHeight / height;
27734                     }
27735                 }
27736                 
27737                 context.scale(scale, scale);
27738                 
27739                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27740                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27741
27742                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27743                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27744                 
27745                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27746                 
27747                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27748                 
27749                 break;
27750             case 180 :
27751                 
27752                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27753                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27754                 
27755                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27756                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27757                 
27758                 var targetWidth = this.minWidth - 2 * x;
27759                 var targetHeight = this.minHeight - 2 * y;
27760                 
27761                 var scale = 1;
27762                 
27763                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27764                     scale = targetWidth / width;
27765                 }
27766                 
27767                 if(x > 0 && y == 0){
27768                     scale = targetHeight / height;
27769                 }
27770                 
27771                 if(x > 0 && y > 0){
27772                     scale = targetWidth / width;
27773                     
27774                     if(width < height){
27775                         scale = targetHeight / height;
27776                     }
27777                 }
27778                 
27779                 context.scale(scale, scale);
27780                 
27781                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27782                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27783
27784                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27785                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27786
27787                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27788                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27789                 
27790                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27791                 
27792                 break;
27793             case 270 :
27794                 
27795                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27796                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27797                 
27798                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27799                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27800                 
27801                 var targetWidth = this.minWidth - 2 * x;
27802                 var targetHeight = this.minHeight - 2 * y;
27803                 
27804                 var scale = 1;
27805                 
27806                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27807                     scale = targetWidth / width;
27808                 }
27809                 
27810                 if(x > 0 && y == 0){
27811                     scale = targetHeight / height;
27812                 }
27813                 
27814                 if(x > 0 && y > 0){
27815                     scale = targetWidth / width;
27816                     
27817                     if(width < height){
27818                         scale = targetHeight / height;
27819                     }
27820                 }
27821                 
27822                 context.scale(scale, scale);
27823                 
27824                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27825                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27826
27827                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27828                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27829                 
27830                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27831                 
27832                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27833                 
27834                 break;
27835             default : 
27836                 break;
27837         }
27838         
27839         this.cropData = canvas.toDataURL(this.cropType);
27840         
27841         if(this.fireEvent('crop', this, this.cropData) !== false){
27842             this.process(this.file, this.cropData);
27843         }
27844         
27845         return;
27846         
27847     },
27848     
27849     setThumbBoxSize : function()
27850     {
27851         var width, height;
27852         
27853         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27854             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27855             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27856             
27857             this.minWidth = width;
27858             this.minHeight = height;
27859             
27860             if(this.rotate == 90 || this.rotate == 270){
27861                 this.minWidth = height;
27862                 this.minHeight = width;
27863             }
27864         }
27865         
27866         height = 300;
27867         width = Math.ceil(this.minWidth * height / this.minHeight);
27868         
27869         if(this.minWidth > this.minHeight){
27870             width = 300;
27871             height = Math.ceil(this.minHeight * width / this.minWidth);
27872         }
27873         
27874         this.thumbEl.setStyle({
27875             width : width + 'px',
27876             height : height + 'px'
27877         });
27878
27879         return;
27880             
27881     },
27882     
27883     setThumbBoxPosition : function()
27884     {
27885         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27886         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27887         
27888         this.thumbEl.setLeft(x);
27889         this.thumbEl.setTop(y);
27890         
27891     },
27892     
27893     baseRotateLevel : function()
27894     {
27895         this.baseRotate = 1;
27896         
27897         if(
27898                 typeof(this.exif) != 'undefined' &&
27899                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27900                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27901         ){
27902             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27903         }
27904         
27905         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27906         
27907     },
27908     
27909     baseScaleLevel : function()
27910     {
27911         var width, height;
27912         
27913         if(this.isDocument){
27914             
27915             if(this.baseRotate == 6 || this.baseRotate == 8){
27916             
27917                 height = this.thumbEl.getHeight();
27918                 this.baseScale = height / this.imageEl.OriginWidth;
27919
27920                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27921                     width = this.thumbEl.getWidth();
27922                     this.baseScale = width / this.imageEl.OriginHeight;
27923                 }
27924
27925                 return;
27926             }
27927
27928             height = this.thumbEl.getHeight();
27929             this.baseScale = height / this.imageEl.OriginHeight;
27930
27931             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27932                 width = this.thumbEl.getWidth();
27933                 this.baseScale = width / this.imageEl.OriginWidth;
27934             }
27935
27936             return;
27937         }
27938         
27939         if(this.baseRotate == 6 || this.baseRotate == 8){
27940             
27941             width = this.thumbEl.getHeight();
27942             this.baseScale = width / this.imageEl.OriginHeight;
27943             
27944             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27945                 height = this.thumbEl.getWidth();
27946                 this.baseScale = height / this.imageEl.OriginHeight;
27947             }
27948             
27949             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27950                 height = this.thumbEl.getWidth();
27951                 this.baseScale = height / this.imageEl.OriginHeight;
27952                 
27953                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27954                     width = this.thumbEl.getHeight();
27955                     this.baseScale = width / this.imageEl.OriginWidth;
27956                 }
27957             }
27958             
27959             return;
27960         }
27961         
27962         width = this.thumbEl.getWidth();
27963         this.baseScale = width / this.imageEl.OriginWidth;
27964         
27965         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27966             height = this.thumbEl.getHeight();
27967             this.baseScale = height / this.imageEl.OriginHeight;
27968         }
27969         
27970         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27971             
27972             height = this.thumbEl.getHeight();
27973             this.baseScale = height / this.imageEl.OriginHeight;
27974             
27975             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27976                 width = this.thumbEl.getWidth();
27977                 this.baseScale = width / this.imageEl.OriginWidth;
27978             }
27979             
27980         }
27981         
27982         return;
27983     },
27984     
27985     getScaleLevel : function()
27986     {
27987         return this.baseScale * Math.pow(1.1, this.scale);
27988     },
27989     
27990     onTouchStart : function(e)
27991     {
27992         if(!this.canvasLoaded){
27993             this.beforeSelectFile(e);
27994             return;
27995         }
27996         
27997         var touches = e.browserEvent.touches;
27998         
27999         if(!touches){
28000             return;
28001         }
28002         
28003         if(touches.length == 1){
28004             this.onMouseDown(e);
28005             return;
28006         }
28007         
28008         if(touches.length != 2){
28009             return;
28010         }
28011         
28012         var coords = [];
28013         
28014         for(var i = 0, finger; finger = touches[i]; i++){
28015             coords.push(finger.pageX, finger.pageY);
28016         }
28017         
28018         var x = Math.pow(coords[0] - coords[2], 2);
28019         var y = Math.pow(coords[1] - coords[3], 2);
28020         
28021         this.startDistance = Math.sqrt(x + y);
28022         
28023         this.startScale = this.scale;
28024         
28025         this.pinching = true;
28026         this.dragable = false;
28027         
28028     },
28029     
28030     onTouchMove : function(e)
28031     {
28032         if(!this.pinching && !this.dragable){
28033             return;
28034         }
28035         
28036         var touches = e.browserEvent.touches;
28037         
28038         if(!touches){
28039             return;
28040         }
28041         
28042         if(this.dragable){
28043             this.onMouseMove(e);
28044             return;
28045         }
28046         
28047         var coords = [];
28048         
28049         for(var i = 0, finger; finger = touches[i]; i++){
28050             coords.push(finger.pageX, finger.pageY);
28051         }
28052         
28053         var x = Math.pow(coords[0] - coords[2], 2);
28054         var y = Math.pow(coords[1] - coords[3], 2);
28055         
28056         this.endDistance = Math.sqrt(x + y);
28057         
28058         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28059         
28060         if(!this.zoomable()){
28061             this.scale = this.startScale;
28062             return;
28063         }
28064         
28065         this.draw();
28066         
28067     },
28068     
28069     onTouchEnd : function(e)
28070     {
28071         this.pinching = false;
28072         this.dragable = false;
28073         
28074     },
28075     
28076     process : function(file, crop)
28077     {
28078         if(this.loadMask){
28079             this.maskEl.mask(this.loadingText);
28080         }
28081         
28082         this.xhr = new XMLHttpRequest();
28083         
28084         file.xhr = this.xhr;
28085
28086         this.xhr.open(this.method, this.url, true);
28087         
28088         var headers = {
28089             "Accept": "application/json",
28090             "Cache-Control": "no-cache",
28091             "X-Requested-With": "XMLHttpRequest"
28092         };
28093         
28094         for (var headerName in headers) {
28095             var headerValue = headers[headerName];
28096             if (headerValue) {
28097                 this.xhr.setRequestHeader(headerName, headerValue);
28098             }
28099         }
28100         
28101         var _this = this;
28102         
28103         this.xhr.onload = function()
28104         {
28105             _this.xhrOnLoad(_this.xhr);
28106         }
28107         
28108         this.xhr.onerror = function()
28109         {
28110             _this.xhrOnError(_this.xhr);
28111         }
28112         
28113         var formData = new FormData();
28114
28115         formData.append('returnHTML', 'NO');
28116         
28117         if(crop){
28118             formData.append('crop', crop);
28119         }
28120         
28121         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28122             formData.append(this.paramName, file, file.name);
28123         }
28124         
28125         if(typeof(file.filename) != 'undefined'){
28126             formData.append('filename', file.filename);
28127         }
28128         
28129         if(typeof(file.mimetype) != 'undefined'){
28130             formData.append('mimetype', file.mimetype);
28131         }
28132         
28133         if(this.fireEvent('arrange', this, formData) != false){
28134             this.xhr.send(formData);
28135         };
28136     },
28137     
28138     xhrOnLoad : function(xhr)
28139     {
28140         if(this.loadMask){
28141             this.maskEl.unmask();
28142         }
28143         
28144         if (xhr.readyState !== 4) {
28145             this.fireEvent('exception', this, xhr);
28146             return;
28147         }
28148
28149         var response = Roo.decode(xhr.responseText);
28150         
28151         if(!response.success){
28152             this.fireEvent('exception', this, xhr);
28153             return;
28154         }
28155         
28156         var response = Roo.decode(xhr.responseText);
28157         
28158         this.fireEvent('upload', this, response);
28159         
28160     },
28161     
28162     xhrOnError : function()
28163     {
28164         if(this.loadMask){
28165             this.maskEl.unmask();
28166         }
28167         
28168         Roo.log('xhr on error');
28169         
28170         var response = Roo.decode(xhr.responseText);
28171           
28172         Roo.log(response);
28173         
28174     },
28175     
28176     prepare : function(file)
28177     {   
28178         if(this.loadMask){
28179             this.maskEl.mask(this.loadingText);
28180         }
28181         
28182         this.file = false;
28183         this.exif = {};
28184         
28185         if(typeof(file) === 'string'){
28186             this.loadCanvas(file);
28187             return;
28188         }
28189         
28190         if(!file || !this.urlAPI){
28191             return;
28192         }
28193         
28194         this.file = file;
28195         this.cropType = file.type;
28196         
28197         var _this = this;
28198         
28199         if(this.fireEvent('prepare', this, this.file) != false){
28200             
28201             var reader = new FileReader();
28202             
28203             reader.onload = function (e) {
28204                 if (e.target.error) {
28205                     Roo.log(e.target.error);
28206                     return;
28207                 }
28208                 
28209                 var buffer = e.target.result,
28210                     dataView = new DataView(buffer),
28211                     offset = 2,
28212                     maxOffset = dataView.byteLength - 4,
28213                     markerBytes,
28214                     markerLength;
28215                 
28216                 if (dataView.getUint16(0) === 0xffd8) {
28217                     while (offset < maxOffset) {
28218                         markerBytes = dataView.getUint16(offset);
28219                         
28220                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28221                             markerLength = dataView.getUint16(offset + 2) + 2;
28222                             if (offset + markerLength > dataView.byteLength) {
28223                                 Roo.log('Invalid meta data: Invalid segment size.');
28224                                 break;
28225                             }
28226                             
28227                             if(markerBytes == 0xffe1){
28228                                 _this.parseExifData(
28229                                     dataView,
28230                                     offset,
28231                                     markerLength
28232                                 );
28233                             }
28234                             
28235                             offset += markerLength;
28236                             
28237                             continue;
28238                         }
28239                         
28240                         break;
28241                     }
28242                     
28243                 }
28244                 
28245                 var url = _this.urlAPI.createObjectURL(_this.file);
28246                 
28247                 _this.loadCanvas(url);
28248                 
28249                 return;
28250             }
28251             
28252             reader.readAsArrayBuffer(this.file);
28253             
28254         }
28255         
28256     },
28257     
28258     parseExifData : function(dataView, offset, length)
28259     {
28260         var tiffOffset = offset + 10,
28261             littleEndian,
28262             dirOffset;
28263     
28264         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28265             // No Exif data, might be XMP data instead
28266             return;
28267         }
28268         
28269         // Check for the ASCII code for "Exif" (0x45786966):
28270         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28271             // No Exif data, might be XMP data instead
28272             return;
28273         }
28274         if (tiffOffset + 8 > dataView.byteLength) {
28275             Roo.log('Invalid Exif data: Invalid segment size.');
28276             return;
28277         }
28278         // Check for the two null bytes:
28279         if (dataView.getUint16(offset + 8) !== 0x0000) {
28280             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28281             return;
28282         }
28283         // Check the byte alignment:
28284         switch (dataView.getUint16(tiffOffset)) {
28285         case 0x4949:
28286             littleEndian = true;
28287             break;
28288         case 0x4D4D:
28289             littleEndian = false;
28290             break;
28291         default:
28292             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28293             return;
28294         }
28295         // Check for the TIFF tag marker (0x002A):
28296         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28297             Roo.log('Invalid Exif data: Missing TIFF marker.');
28298             return;
28299         }
28300         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28301         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28302         
28303         this.parseExifTags(
28304             dataView,
28305             tiffOffset,
28306             tiffOffset + dirOffset,
28307             littleEndian
28308         );
28309     },
28310     
28311     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28312     {
28313         var tagsNumber,
28314             dirEndOffset,
28315             i;
28316         if (dirOffset + 6 > dataView.byteLength) {
28317             Roo.log('Invalid Exif data: Invalid directory offset.');
28318             return;
28319         }
28320         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28321         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28322         if (dirEndOffset + 4 > dataView.byteLength) {
28323             Roo.log('Invalid Exif data: Invalid directory size.');
28324             return;
28325         }
28326         for (i = 0; i < tagsNumber; i += 1) {
28327             this.parseExifTag(
28328                 dataView,
28329                 tiffOffset,
28330                 dirOffset + 2 + 12 * i, // tag offset
28331                 littleEndian
28332             );
28333         }
28334         // Return the offset to the next directory:
28335         return dataView.getUint32(dirEndOffset, littleEndian);
28336     },
28337     
28338     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28339     {
28340         var tag = dataView.getUint16(offset, littleEndian);
28341         
28342         this.exif[tag] = this.getExifValue(
28343             dataView,
28344             tiffOffset,
28345             offset,
28346             dataView.getUint16(offset + 2, littleEndian), // tag type
28347             dataView.getUint32(offset + 4, littleEndian), // tag length
28348             littleEndian
28349         );
28350     },
28351     
28352     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28353     {
28354         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28355             tagSize,
28356             dataOffset,
28357             values,
28358             i,
28359             str,
28360             c;
28361     
28362         if (!tagType) {
28363             Roo.log('Invalid Exif data: Invalid tag type.');
28364             return;
28365         }
28366         
28367         tagSize = tagType.size * length;
28368         // Determine if the value is contained in the dataOffset bytes,
28369         // or if the value at the dataOffset is a pointer to the actual data:
28370         dataOffset = tagSize > 4 ?
28371                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28372         if (dataOffset + tagSize > dataView.byteLength) {
28373             Roo.log('Invalid Exif data: Invalid data offset.');
28374             return;
28375         }
28376         if (length === 1) {
28377             return tagType.getValue(dataView, dataOffset, littleEndian);
28378         }
28379         values = [];
28380         for (i = 0; i < length; i += 1) {
28381             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28382         }
28383         
28384         if (tagType.ascii) {
28385             str = '';
28386             // Concatenate the chars:
28387             for (i = 0; i < values.length; i += 1) {
28388                 c = values[i];
28389                 // Ignore the terminating NULL byte(s):
28390                 if (c === '\u0000') {
28391                     break;
28392                 }
28393                 str += c;
28394             }
28395             return str;
28396         }
28397         return values;
28398     }
28399     
28400 });
28401
28402 Roo.apply(Roo.bootstrap.UploadCropbox, {
28403     tags : {
28404         'Orientation': 0x0112
28405     },
28406     
28407     Orientation: {
28408             1: 0, //'top-left',
28409 //            2: 'top-right',
28410             3: 180, //'bottom-right',
28411 //            4: 'bottom-left',
28412 //            5: 'left-top',
28413             6: 90, //'right-top',
28414 //            7: 'right-bottom',
28415             8: 270 //'left-bottom'
28416     },
28417     
28418     exifTagTypes : {
28419         // byte, 8-bit unsigned int:
28420         1: {
28421             getValue: function (dataView, dataOffset) {
28422                 return dataView.getUint8(dataOffset);
28423             },
28424             size: 1
28425         },
28426         // ascii, 8-bit byte:
28427         2: {
28428             getValue: function (dataView, dataOffset) {
28429                 return String.fromCharCode(dataView.getUint8(dataOffset));
28430             },
28431             size: 1,
28432             ascii: true
28433         },
28434         // short, 16 bit int:
28435         3: {
28436             getValue: function (dataView, dataOffset, littleEndian) {
28437                 return dataView.getUint16(dataOffset, littleEndian);
28438             },
28439             size: 2
28440         },
28441         // long, 32 bit int:
28442         4: {
28443             getValue: function (dataView, dataOffset, littleEndian) {
28444                 return dataView.getUint32(dataOffset, littleEndian);
28445             },
28446             size: 4
28447         },
28448         // rational = two long values, first is numerator, second is denominator:
28449         5: {
28450             getValue: function (dataView, dataOffset, littleEndian) {
28451                 return dataView.getUint32(dataOffset, littleEndian) /
28452                     dataView.getUint32(dataOffset + 4, littleEndian);
28453             },
28454             size: 8
28455         },
28456         // slong, 32 bit signed int:
28457         9: {
28458             getValue: function (dataView, dataOffset, littleEndian) {
28459                 return dataView.getInt32(dataOffset, littleEndian);
28460             },
28461             size: 4
28462         },
28463         // srational, two slongs, first is numerator, second is denominator:
28464         10: {
28465             getValue: function (dataView, dataOffset, littleEndian) {
28466                 return dataView.getInt32(dataOffset, littleEndian) /
28467                     dataView.getInt32(dataOffset + 4, littleEndian);
28468             },
28469             size: 8
28470         }
28471     },
28472     
28473     footer : {
28474         STANDARD : [
28475             {
28476                 tag : 'div',
28477                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28478                 action : 'rotate-left',
28479                 cn : [
28480                     {
28481                         tag : 'button',
28482                         cls : 'btn btn-default',
28483                         html : '<i class="fa fa-undo"></i>'
28484                     }
28485                 ]
28486             },
28487             {
28488                 tag : 'div',
28489                 cls : 'btn-group roo-upload-cropbox-picture',
28490                 action : 'picture',
28491                 cn : [
28492                     {
28493                         tag : 'button',
28494                         cls : 'btn btn-default',
28495                         html : '<i class="fa fa-picture-o"></i>'
28496                     }
28497                 ]
28498             },
28499             {
28500                 tag : 'div',
28501                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28502                 action : 'rotate-right',
28503                 cn : [
28504                     {
28505                         tag : 'button',
28506                         cls : 'btn btn-default',
28507                         html : '<i class="fa fa-repeat"></i>'
28508                     }
28509                 ]
28510             }
28511         ],
28512         DOCUMENT : [
28513             {
28514                 tag : 'div',
28515                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28516                 action : 'rotate-left',
28517                 cn : [
28518                     {
28519                         tag : 'button',
28520                         cls : 'btn btn-default',
28521                         html : '<i class="fa fa-undo"></i>'
28522                     }
28523                 ]
28524             },
28525             {
28526                 tag : 'div',
28527                 cls : 'btn-group roo-upload-cropbox-download',
28528                 action : 'download',
28529                 cn : [
28530                     {
28531                         tag : 'button',
28532                         cls : 'btn btn-default',
28533                         html : '<i class="fa fa-download"></i>'
28534                     }
28535                 ]
28536             },
28537             {
28538                 tag : 'div',
28539                 cls : 'btn-group roo-upload-cropbox-crop',
28540                 action : 'crop',
28541                 cn : [
28542                     {
28543                         tag : 'button',
28544                         cls : 'btn btn-default',
28545                         html : '<i class="fa fa-crop"></i>'
28546                     }
28547                 ]
28548             },
28549             {
28550                 tag : 'div',
28551                 cls : 'btn-group roo-upload-cropbox-trash',
28552                 action : 'trash',
28553                 cn : [
28554                     {
28555                         tag : 'button',
28556                         cls : 'btn btn-default',
28557                         html : '<i class="fa fa-trash"></i>'
28558                     }
28559                 ]
28560             },
28561             {
28562                 tag : 'div',
28563                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28564                 action : 'rotate-right',
28565                 cn : [
28566                     {
28567                         tag : 'button',
28568                         cls : 'btn btn-default',
28569                         html : '<i class="fa fa-repeat"></i>'
28570                     }
28571                 ]
28572             }
28573         ],
28574         ROTATOR : [
28575             {
28576                 tag : 'div',
28577                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28578                 action : 'rotate-left',
28579                 cn : [
28580                     {
28581                         tag : 'button',
28582                         cls : 'btn btn-default',
28583                         html : '<i class="fa fa-undo"></i>'
28584                     }
28585                 ]
28586             },
28587             {
28588                 tag : 'div',
28589                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28590                 action : 'rotate-right',
28591                 cn : [
28592                     {
28593                         tag : 'button',
28594                         cls : 'btn btn-default',
28595                         html : '<i class="fa fa-repeat"></i>'
28596                     }
28597                 ]
28598             }
28599         ]
28600     }
28601 });
28602
28603 /*
28604 * Licence: LGPL
28605 */
28606
28607 /**
28608  * @class Roo.bootstrap.DocumentManager
28609  * @extends Roo.bootstrap.Component
28610  * Bootstrap DocumentManager class
28611  * @cfg {String} paramName default 'imageUpload'
28612  * @cfg {String} toolTipName default 'filename'
28613  * @cfg {String} method default POST
28614  * @cfg {String} url action url
28615  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28616  * @cfg {Boolean} multiple multiple upload default true
28617  * @cfg {Number} thumbSize default 300
28618  * @cfg {String} fieldLabel
28619  * @cfg {Number} labelWidth default 4
28620  * @cfg {String} labelAlign (left|top) default left
28621  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28622 * @cfg {Number} labellg set the width of label (1-12)
28623  * @cfg {Number} labelmd set the width of label (1-12)
28624  * @cfg {Number} labelsm set the width of label (1-12)
28625  * @cfg {Number} labelxs set the width of label (1-12)
28626  * 
28627  * @constructor
28628  * Create a new DocumentManager
28629  * @param {Object} config The config object
28630  */
28631
28632 Roo.bootstrap.DocumentManager = function(config){
28633     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28634     
28635     this.files = [];
28636     this.delegates = [];
28637     
28638     this.addEvents({
28639         /**
28640          * @event initial
28641          * Fire when initial the DocumentManager
28642          * @param {Roo.bootstrap.DocumentManager} this
28643          */
28644         "initial" : true,
28645         /**
28646          * @event inspect
28647          * inspect selected file
28648          * @param {Roo.bootstrap.DocumentManager} this
28649          * @param {File} file
28650          */
28651         "inspect" : true,
28652         /**
28653          * @event exception
28654          * Fire when xhr load exception
28655          * @param {Roo.bootstrap.DocumentManager} this
28656          * @param {XMLHttpRequest} xhr
28657          */
28658         "exception" : true,
28659         /**
28660          * @event afterupload
28661          * Fire when xhr load exception
28662          * @param {Roo.bootstrap.DocumentManager} this
28663          * @param {XMLHttpRequest} xhr
28664          */
28665         "afterupload" : true,
28666         /**
28667          * @event prepare
28668          * prepare the form data
28669          * @param {Roo.bootstrap.DocumentManager} this
28670          * @param {Object} formData
28671          */
28672         "prepare" : true,
28673         /**
28674          * @event remove
28675          * Fire when remove the file
28676          * @param {Roo.bootstrap.DocumentManager} this
28677          * @param {Object} file
28678          */
28679         "remove" : true,
28680         /**
28681          * @event refresh
28682          * Fire after refresh the file
28683          * @param {Roo.bootstrap.DocumentManager} this
28684          */
28685         "refresh" : true,
28686         /**
28687          * @event click
28688          * Fire after click the image
28689          * @param {Roo.bootstrap.DocumentManager} this
28690          * @param {Object} file
28691          */
28692         "click" : true,
28693         /**
28694          * @event edit
28695          * Fire when upload a image and editable set to true
28696          * @param {Roo.bootstrap.DocumentManager} this
28697          * @param {Object} file
28698          */
28699         "edit" : true,
28700         /**
28701          * @event beforeselectfile
28702          * Fire before select file
28703          * @param {Roo.bootstrap.DocumentManager} this
28704          */
28705         "beforeselectfile" : true,
28706         /**
28707          * @event process
28708          * Fire before process file
28709          * @param {Roo.bootstrap.DocumentManager} this
28710          * @param {Object} file
28711          */
28712         "process" : true,
28713         /**
28714          * @event previewrendered
28715          * Fire when preview rendered
28716          * @param {Roo.bootstrap.DocumentManager} this
28717          * @param {Object} file
28718          */
28719         "previewrendered" : true,
28720         /**
28721          */
28722         "previewResize" : true
28723         
28724     });
28725 };
28726
28727 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28728     
28729     boxes : 0,
28730     inputName : '',
28731     thumbSize : 300,
28732     multiple : true,
28733     files : false,
28734     method : 'POST',
28735     url : '',
28736     paramName : 'imageUpload',
28737     toolTipName : 'filename',
28738     fieldLabel : '',
28739     labelWidth : 4,
28740     labelAlign : 'left',
28741     editable : true,
28742     delegates : false,
28743     xhr : false, 
28744     
28745     labellg : 0,
28746     labelmd : 0,
28747     labelsm : 0,
28748     labelxs : 0,
28749     
28750     getAutoCreate : function()
28751     {   
28752         var managerWidget = {
28753             tag : 'div',
28754             cls : 'roo-document-manager',
28755             cn : [
28756                 {
28757                     tag : 'input',
28758                     cls : 'roo-document-manager-selector',
28759                     type : 'file'
28760                 },
28761                 {
28762                     tag : 'div',
28763                     cls : 'roo-document-manager-uploader',
28764                     cn : [
28765                         {
28766                             tag : 'div',
28767                             cls : 'roo-document-manager-upload-btn',
28768                             html : '<i class="fa fa-plus"></i>'
28769                         }
28770                     ]
28771                     
28772                 }
28773             ]
28774         };
28775         
28776         var content = [
28777             {
28778                 tag : 'div',
28779                 cls : 'column col-md-12',
28780                 cn : managerWidget
28781             }
28782         ];
28783         
28784         if(this.fieldLabel.length){
28785             
28786             content = [
28787                 {
28788                     tag : 'div',
28789                     cls : 'column col-md-12',
28790                     html : this.fieldLabel
28791                 },
28792                 {
28793                     tag : 'div',
28794                     cls : 'column col-md-12',
28795                     cn : managerWidget
28796                 }
28797             ];
28798
28799             if(this.labelAlign == 'left'){
28800                 content = [
28801                     {
28802                         tag : 'div',
28803                         cls : 'column',
28804                         html : this.fieldLabel
28805                     },
28806                     {
28807                         tag : 'div',
28808                         cls : 'column',
28809                         cn : managerWidget
28810                     }
28811                 ];
28812                 
28813                 if(this.labelWidth > 12){
28814                     content[0].style = "width: " + this.labelWidth + 'px';
28815                 }
28816
28817                 if(this.labelWidth < 13 && this.labelmd == 0){
28818                     this.labelmd = this.labelWidth;
28819                 }
28820
28821                 if(this.labellg > 0){
28822                     content[0].cls += ' col-lg-' + this.labellg;
28823                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28824                 }
28825
28826                 if(this.labelmd > 0){
28827                     content[0].cls += ' col-md-' + this.labelmd;
28828                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28829                 }
28830
28831                 if(this.labelsm > 0){
28832                     content[0].cls += ' col-sm-' + this.labelsm;
28833                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28834                 }
28835
28836                 if(this.labelxs > 0){
28837                     content[0].cls += ' col-xs-' + this.labelxs;
28838                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28839                 }
28840                 
28841             }
28842         }
28843         
28844         var cfg = {
28845             tag : 'div',
28846             cls : 'row clearfix',
28847             cn : content
28848         };
28849         
28850         return cfg;
28851         
28852     },
28853     
28854     initEvents : function()
28855     {
28856         this.managerEl = this.el.select('.roo-document-manager', true).first();
28857         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28858         
28859         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28860         this.selectorEl.hide();
28861         
28862         if(this.multiple){
28863             this.selectorEl.attr('multiple', 'multiple');
28864         }
28865         
28866         this.selectorEl.on('change', this.onFileSelected, this);
28867         
28868         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28869         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28870         
28871         this.uploader.on('click', this.onUploaderClick, this);
28872         
28873         this.renderProgressDialog();
28874         
28875         var _this = this;
28876         
28877         window.addEventListener("resize", function() { _this.refresh(); } );
28878         
28879         this.fireEvent('initial', this);
28880     },
28881     
28882     renderProgressDialog : function()
28883     {
28884         var _this = this;
28885         
28886         this.progressDialog = new Roo.bootstrap.Modal({
28887             cls : 'roo-document-manager-progress-dialog',
28888             allow_close : false,
28889             title : '',
28890             buttons : [
28891                 {
28892                     name  :'cancel',
28893                     weight : 'danger',
28894                     html : 'Cancel'
28895                 }
28896             ], 
28897             listeners : { 
28898                 btnclick : function() {
28899                     _this.uploadCancel();
28900                     this.hide();
28901                 }
28902             }
28903         });
28904          
28905         this.progressDialog.render(Roo.get(document.body));
28906          
28907         this.progress = new Roo.bootstrap.Progress({
28908             cls : 'roo-document-manager-progress',
28909             active : true,
28910             striped : true
28911         });
28912         
28913         this.progress.render(this.progressDialog.getChildContainer());
28914         
28915         this.progressBar = new Roo.bootstrap.ProgressBar({
28916             cls : 'roo-document-manager-progress-bar',
28917             aria_valuenow : 0,
28918             aria_valuemin : 0,
28919             aria_valuemax : 12,
28920             panel : 'success'
28921         });
28922         
28923         this.progressBar.render(this.progress.getChildContainer());
28924     },
28925     
28926     onUploaderClick : function(e)
28927     {
28928         e.preventDefault();
28929      
28930         if(this.fireEvent('beforeselectfile', this) != false){
28931             this.selectorEl.dom.click();
28932         }
28933         
28934     },
28935     
28936     onFileSelected : function(e)
28937     {
28938         e.preventDefault();
28939         
28940         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28941             return;
28942         }
28943         
28944         Roo.each(this.selectorEl.dom.files, function(file){
28945             if(this.fireEvent('inspect', this, file) != false){
28946                 this.files.push(file);
28947             }
28948         }, this);
28949         
28950         this.queue();
28951         
28952     },
28953     
28954     queue : function()
28955     {
28956         this.selectorEl.dom.value = '';
28957         
28958         if(!this.files || !this.files.length){
28959             return;
28960         }
28961         
28962         if(this.boxes > 0 && this.files.length > this.boxes){
28963             this.files = this.files.slice(0, this.boxes);
28964         }
28965         
28966         this.uploader.show();
28967         
28968         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28969             this.uploader.hide();
28970         }
28971         
28972         var _this = this;
28973         
28974         var files = [];
28975         
28976         var docs = [];
28977         
28978         Roo.each(this.files, function(file){
28979             
28980             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28981                 var f = this.renderPreview(file);
28982                 files.push(f);
28983                 return;
28984             }
28985             
28986             if(file.type.indexOf('image') != -1){
28987                 this.delegates.push(
28988                     (function(){
28989                         _this.process(file);
28990                     }).createDelegate(this)
28991                 );
28992         
28993                 return;
28994             }
28995             
28996             docs.push(
28997                 (function(){
28998                     _this.process(file);
28999                 }).createDelegate(this)
29000             );
29001             
29002         }, this);
29003         
29004         this.files = files;
29005         
29006         this.delegates = this.delegates.concat(docs);
29007         
29008         if(!this.delegates.length){
29009             this.refresh();
29010             return;
29011         }
29012         
29013         this.progressBar.aria_valuemax = this.delegates.length;
29014         
29015         this.arrange();
29016         
29017         return;
29018     },
29019     
29020     arrange : function()
29021     {
29022         if(!this.delegates.length){
29023             this.progressDialog.hide();
29024             this.refresh();
29025             return;
29026         }
29027         
29028         var delegate = this.delegates.shift();
29029         
29030         this.progressDialog.show();
29031         
29032         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29033         
29034         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29035         
29036         delegate();
29037     },
29038     
29039     refresh : function()
29040     {
29041         this.uploader.show();
29042         
29043         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29044             this.uploader.hide();
29045         }
29046         
29047         Roo.isTouch ? this.closable(false) : this.closable(true);
29048         
29049         this.fireEvent('refresh', this);
29050     },
29051     
29052     onRemove : function(e, el, o)
29053     {
29054         e.preventDefault();
29055         
29056         this.fireEvent('remove', this, o);
29057         
29058     },
29059     
29060     remove : function(o)
29061     {
29062         var files = [];
29063         
29064         Roo.each(this.files, function(file){
29065             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29066                 files.push(file);
29067                 return;
29068             }
29069
29070             o.target.remove();
29071
29072         }, this);
29073         
29074         this.files = files;
29075         
29076         this.refresh();
29077     },
29078     
29079     clear : function()
29080     {
29081         Roo.each(this.files, function(file){
29082             if(!file.target){
29083                 return;
29084             }
29085             
29086             file.target.remove();
29087
29088         }, this);
29089         
29090         this.files = [];
29091         
29092         this.refresh();
29093     },
29094     
29095     onClick : function(e, el, o)
29096     {
29097         e.preventDefault();
29098         
29099         this.fireEvent('click', this, o);
29100         
29101     },
29102     
29103     closable : function(closable)
29104     {
29105         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29106             
29107             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29108             
29109             if(closable){
29110                 el.show();
29111                 return;
29112             }
29113             
29114             el.hide();
29115             
29116         }, this);
29117     },
29118     
29119     xhrOnLoad : function(xhr)
29120     {
29121         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29122             el.remove();
29123         }, this);
29124         
29125         if (xhr.readyState !== 4) {
29126             this.arrange();
29127             this.fireEvent('exception', this, xhr);
29128             return;
29129         }
29130
29131         var response = Roo.decode(xhr.responseText);
29132         
29133         if(!response.success){
29134             this.arrange();
29135             this.fireEvent('exception', this, xhr);
29136             return;
29137         }
29138         
29139         var file = this.renderPreview(response.data);
29140         
29141         this.files.push(file);
29142         
29143         this.arrange();
29144         
29145         this.fireEvent('afterupload', this, xhr);
29146         
29147     },
29148     
29149     xhrOnError : function(xhr)
29150     {
29151         Roo.log('xhr on error');
29152         
29153         var response = Roo.decode(xhr.responseText);
29154           
29155         Roo.log(response);
29156         
29157         this.arrange();
29158     },
29159     
29160     process : function(file)
29161     {
29162         if(this.fireEvent('process', this, file) !== false){
29163             if(this.editable && file.type.indexOf('image') != -1){
29164                 this.fireEvent('edit', this, file);
29165                 return;
29166             }
29167
29168             this.uploadStart(file, false);
29169
29170             return;
29171         }
29172         
29173     },
29174     
29175     uploadStart : function(file, crop)
29176     {
29177         this.xhr = new XMLHttpRequest();
29178         
29179         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29180             this.arrange();
29181             return;
29182         }
29183         
29184         file.xhr = this.xhr;
29185             
29186         this.managerEl.createChild({
29187             tag : 'div',
29188             cls : 'roo-document-manager-loading',
29189             cn : [
29190                 {
29191                     tag : 'div',
29192                     tooltip : file.name,
29193                     cls : 'roo-document-manager-thumb',
29194                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29195                 }
29196             ]
29197
29198         });
29199
29200         this.xhr.open(this.method, this.url, true);
29201         
29202         var headers = {
29203             "Accept": "application/json",
29204             "Cache-Control": "no-cache",
29205             "X-Requested-With": "XMLHttpRequest"
29206         };
29207         
29208         for (var headerName in headers) {
29209             var headerValue = headers[headerName];
29210             if (headerValue) {
29211                 this.xhr.setRequestHeader(headerName, headerValue);
29212             }
29213         }
29214         
29215         var _this = this;
29216         
29217         this.xhr.onload = function()
29218         {
29219             _this.xhrOnLoad(_this.xhr);
29220         }
29221         
29222         this.xhr.onerror = function()
29223         {
29224             _this.xhrOnError(_this.xhr);
29225         }
29226         
29227         var formData = new FormData();
29228
29229         formData.append('returnHTML', 'NO');
29230         
29231         if(crop){
29232             formData.append('crop', crop);
29233         }
29234         
29235         formData.append(this.paramName, file, file.name);
29236         
29237         var options = {
29238             file : file, 
29239             manually : false
29240         };
29241         
29242         if(this.fireEvent('prepare', this, formData, options) != false){
29243             
29244             if(options.manually){
29245                 return;
29246             }
29247             
29248             this.xhr.send(formData);
29249             return;
29250         };
29251         
29252         this.uploadCancel();
29253     },
29254     
29255     uploadCancel : function()
29256     {
29257         if (this.xhr) {
29258             this.xhr.abort();
29259         }
29260         
29261         this.delegates = [];
29262         
29263         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29264             el.remove();
29265         }, this);
29266         
29267         this.arrange();
29268     },
29269     
29270     renderPreview : function(file)
29271     {
29272         if(typeof(file.target) != 'undefined' && file.target){
29273             return file;
29274         }
29275         
29276         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29277         
29278         var previewEl = this.managerEl.createChild({
29279             tag : 'div',
29280             cls : 'roo-document-manager-preview',
29281             cn : [
29282                 {
29283                     tag : 'div',
29284                     tooltip : file[this.toolTipName],
29285                     cls : 'roo-document-manager-thumb',
29286                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29287                 },
29288                 {
29289                     tag : 'button',
29290                     cls : 'close',
29291                     html : '<i class="fa fa-times-circle"></i>'
29292                 }
29293             ]
29294         });
29295
29296         var close = previewEl.select('button.close', true).first();
29297
29298         close.on('click', this.onRemove, this, file);
29299
29300         file.target = previewEl;
29301
29302         var image = previewEl.select('img', true).first();
29303         
29304         var _this = this;
29305         
29306         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29307         
29308         image.on('click', this.onClick, this, file);
29309         
29310         this.fireEvent('previewrendered', this, file);
29311         
29312         return file;
29313         
29314     },
29315     
29316     onPreviewLoad : function(file, image)
29317     {
29318         if(typeof(file.target) == 'undefined' || !file.target){
29319             return;
29320         }
29321         
29322         var width = image.dom.naturalWidth || image.dom.width;
29323         var height = image.dom.naturalHeight || image.dom.height;
29324         
29325         if(!this.previewResize) {
29326             return;
29327         }
29328         
29329         if(width > height){
29330             file.target.addClass('wide');
29331             return;
29332         }
29333         
29334         file.target.addClass('tall');
29335         return;
29336         
29337     },
29338     
29339     uploadFromSource : function(file, crop)
29340     {
29341         this.xhr = new XMLHttpRequest();
29342         
29343         this.managerEl.createChild({
29344             tag : 'div',
29345             cls : 'roo-document-manager-loading',
29346             cn : [
29347                 {
29348                     tag : 'div',
29349                     tooltip : file.name,
29350                     cls : 'roo-document-manager-thumb',
29351                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29352                 }
29353             ]
29354
29355         });
29356
29357         this.xhr.open(this.method, this.url, true);
29358         
29359         var headers = {
29360             "Accept": "application/json",
29361             "Cache-Control": "no-cache",
29362             "X-Requested-With": "XMLHttpRequest"
29363         };
29364         
29365         for (var headerName in headers) {
29366             var headerValue = headers[headerName];
29367             if (headerValue) {
29368                 this.xhr.setRequestHeader(headerName, headerValue);
29369             }
29370         }
29371         
29372         var _this = this;
29373         
29374         this.xhr.onload = function()
29375         {
29376             _this.xhrOnLoad(_this.xhr);
29377         }
29378         
29379         this.xhr.onerror = function()
29380         {
29381             _this.xhrOnError(_this.xhr);
29382         }
29383         
29384         var formData = new FormData();
29385
29386         formData.append('returnHTML', 'NO');
29387         
29388         formData.append('crop', crop);
29389         
29390         if(typeof(file.filename) != 'undefined'){
29391             formData.append('filename', file.filename);
29392         }
29393         
29394         if(typeof(file.mimetype) != 'undefined'){
29395             formData.append('mimetype', file.mimetype);
29396         }
29397         
29398         Roo.log(formData);
29399         
29400         if(this.fireEvent('prepare', this, formData) != false){
29401             this.xhr.send(formData);
29402         };
29403     }
29404 });
29405
29406 /*
29407 * Licence: LGPL
29408 */
29409
29410 /**
29411  * @class Roo.bootstrap.DocumentViewer
29412  * @extends Roo.bootstrap.Component
29413  * Bootstrap DocumentViewer class
29414  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29415  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29416  * 
29417  * @constructor
29418  * Create a new DocumentViewer
29419  * @param {Object} config The config object
29420  */
29421
29422 Roo.bootstrap.DocumentViewer = function(config){
29423     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29424     
29425     this.addEvents({
29426         /**
29427          * @event initial
29428          * Fire after initEvent
29429          * @param {Roo.bootstrap.DocumentViewer} this
29430          */
29431         "initial" : true,
29432         /**
29433          * @event click
29434          * Fire after click
29435          * @param {Roo.bootstrap.DocumentViewer} this
29436          */
29437         "click" : true,
29438         /**
29439          * @event download
29440          * Fire after download button
29441          * @param {Roo.bootstrap.DocumentViewer} this
29442          */
29443         "download" : true,
29444         /**
29445          * @event trash
29446          * Fire after trash button
29447          * @param {Roo.bootstrap.DocumentViewer} this
29448          */
29449         "trash" : true
29450         
29451     });
29452 };
29453
29454 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29455     
29456     showDownload : true,
29457     
29458     showTrash : true,
29459     
29460     getAutoCreate : function()
29461     {
29462         var cfg = {
29463             tag : 'div',
29464             cls : 'roo-document-viewer',
29465             cn : [
29466                 {
29467                     tag : 'div',
29468                     cls : 'roo-document-viewer-body',
29469                     cn : [
29470                         {
29471                             tag : 'div',
29472                             cls : 'roo-document-viewer-thumb',
29473                             cn : [
29474                                 {
29475                                     tag : 'img',
29476                                     cls : 'roo-document-viewer-image'
29477                                 }
29478                             ]
29479                         }
29480                     ]
29481                 },
29482                 {
29483                     tag : 'div',
29484                     cls : 'roo-document-viewer-footer',
29485                     cn : {
29486                         tag : 'div',
29487                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29488                         cn : [
29489                             {
29490                                 tag : 'div',
29491                                 cls : 'btn-group roo-document-viewer-download',
29492                                 cn : [
29493                                     {
29494                                         tag : 'button',
29495                                         cls : 'btn btn-default',
29496                                         html : '<i class="fa fa-download"></i>'
29497                                     }
29498                                 ]
29499                             },
29500                             {
29501                                 tag : 'div',
29502                                 cls : 'btn-group roo-document-viewer-trash',
29503                                 cn : [
29504                                     {
29505                                         tag : 'button',
29506                                         cls : 'btn btn-default',
29507                                         html : '<i class="fa fa-trash"></i>'
29508                                     }
29509                                 ]
29510                             }
29511                         ]
29512                     }
29513                 }
29514             ]
29515         };
29516         
29517         return cfg;
29518     },
29519     
29520     initEvents : function()
29521     {
29522         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29523         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29524         
29525         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29526         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29527         
29528         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29529         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29530         
29531         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29532         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29533         
29534         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29535         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29536         
29537         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29538         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29539         
29540         this.bodyEl.on('click', this.onClick, this);
29541         this.downloadBtn.on('click', this.onDownload, this);
29542         this.trashBtn.on('click', this.onTrash, this);
29543         
29544         this.downloadBtn.hide();
29545         this.trashBtn.hide();
29546         
29547         if(this.showDownload){
29548             this.downloadBtn.show();
29549         }
29550         
29551         if(this.showTrash){
29552             this.trashBtn.show();
29553         }
29554         
29555         if(!this.showDownload && !this.showTrash) {
29556             this.footerEl.hide();
29557         }
29558         
29559     },
29560     
29561     initial : function()
29562     {
29563         this.fireEvent('initial', this);
29564         
29565     },
29566     
29567     onClick : function(e)
29568     {
29569         e.preventDefault();
29570         
29571         this.fireEvent('click', this);
29572     },
29573     
29574     onDownload : function(e)
29575     {
29576         e.preventDefault();
29577         
29578         this.fireEvent('download', this);
29579     },
29580     
29581     onTrash : function(e)
29582     {
29583         e.preventDefault();
29584         
29585         this.fireEvent('trash', this);
29586     }
29587     
29588 });
29589 /*
29590  * - LGPL
29591  *
29592  * nav progress bar
29593  * 
29594  */
29595
29596 /**
29597  * @class Roo.bootstrap.NavProgressBar
29598  * @extends Roo.bootstrap.Component
29599  * Bootstrap NavProgressBar class
29600  * 
29601  * @constructor
29602  * Create a new nav progress bar
29603  * @param {Object} config The config object
29604  */
29605
29606 Roo.bootstrap.NavProgressBar = function(config){
29607     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29608
29609     this.bullets = this.bullets || [];
29610    
29611 //    Roo.bootstrap.NavProgressBar.register(this);
29612      this.addEvents({
29613         /**
29614              * @event changed
29615              * Fires when the active item changes
29616              * @param {Roo.bootstrap.NavProgressBar} this
29617              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29618              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29619          */
29620         'changed': true
29621      });
29622     
29623 };
29624
29625 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29626     
29627     bullets : [],
29628     barItems : [],
29629     
29630     getAutoCreate : function()
29631     {
29632         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29633         
29634         cfg = {
29635             tag : 'div',
29636             cls : 'roo-navigation-bar-group',
29637             cn : [
29638                 {
29639                     tag : 'div',
29640                     cls : 'roo-navigation-top-bar'
29641                 },
29642                 {
29643                     tag : 'div',
29644                     cls : 'roo-navigation-bullets-bar',
29645                     cn : [
29646                         {
29647                             tag : 'ul',
29648                             cls : 'roo-navigation-bar'
29649                         }
29650                     ]
29651                 },
29652                 
29653                 {
29654                     tag : 'div',
29655                     cls : 'roo-navigation-bottom-bar'
29656                 }
29657             ]
29658             
29659         };
29660         
29661         return cfg;
29662         
29663     },
29664     
29665     initEvents: function() 
29666     {
29667         
29668     },
29669     
29670     onRender : function(ct, position) 
29671     {
29672         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29673         
29674         if(this.bullets.length){
29675             Roo.each(this.bullets, function(b){
29676                this.addItem(b);
29677             }, this);
29678         }
29679         
29680         this.format();
29681         
29682     },
29683     
29684     addItem : function(cfg)
29685     {
29686         var item = new Roo.bootstrap.NavProgressItem(cfg);
29687         
29688         item.parentId = this.id;
29689         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29690         
29691         if(cfg.html){
29692             var top = new Roo.bootstrap.Element({
29693                 tag : 'div',
29694                 cls : 'roo-navigation-bar-text'
29695             });
29696             
29697             var bottom = new Roo.bootstrap.Element({
29698                 tag : 'div',
29699                 cls : 'roo-navigation-bar-text'
29700             });
29701             
29702             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29703             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29704             
29705             var topText = new Roo.bootstrap.Element({
29706                 tag : 'span',
29707                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29708             });
29709             
29710             var bottomText = new Roo.bootstrap.Element({
29711                 tag : 'span',
29712                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29713             });
29714             
29715             topText.onRender(top.el, null);
29716             bottomText.onRender(bottom.el, null);
29717             
29718             item.topEl = top;
29719             item.bottomEl = bottom;
29720         }
29721         
29722         this.barItems.push(item);
29723         
29724         return item;
29725     },
29726     
29727     getActive : function()
29728     {
29729         var active = false;
29730         
29731         Roo.each(this.barItems, function(v){
29732             
29733             if (!v.isActive()) {
29734                 return;
29735             }
29736             
29737             active = v;
29738             return false;
29739             
29740         });
29741         
29742         return active;
29743     },
29744     
29745     setActiveItem : function(item)
29746     {
29747         var prev = false;
29748         
29749         Roo.each(this.barItems, function(v){
29750             if (v.rid == item.rid) {
29751                 return ;
29752             }
29753             
29754             if (v.isActive()) {
29755                 v.setActive(false);
29756                 prev = v;
29757             }
29758         });
29759
29760         item.setActive(true);
29761         
29762         this.fireEvent('changed', this, item, prev);
29763     },
29764     
29765     getBarItem: function(rid)
29766     {
29767         var ret = false;
29768         
29769         Roo.each(this.barItems, function(e) {
29770             if (e.rid != rid) {
29771                 return;
29772             }
29773             
29774             ret =  e;
29775             return false;
29776         });
29777         
29778         return ret;
29779     },
29780     
29781     indexOfItem : function(item)
29782     {
29783         var index = false;
29784         
29785         Roo.each(this.barItems, function(v, i){
29786             
29787             if (v.rid != item.rid) {
29788                 return;
29789             }
29790             
29791             index = i;
29792             return false
29793         });
29794         
29795         return index;
29796     },
29797     
29798     setActiveNext : function()
29799     {
29800         var i = this.indexOfItem(this.getActive());
29801         
29802         if (i > this.barItems.length) {
29803             return;
29804         }
29805         
29806         this.setActiveItem(this.barItems[i+1]);
29807     },
29808     
29809     setActivePrev : function()
29810     {
29811         var i = this.indexOfItem(this.getActive());
29812         
29813         if (i  < 1) {
29814             return;
29815         }
29816         
29817         this.setActiveItem(this.barItems[i-1]);
29818     },
29819     
29820     format : function()
29821     {
29822         if(!this.barItems.length){
29823             return;
29824         }
29825      
29826         var width = 100 / this.barItems.length;
29827         
29828         Roo.each(this.barItems, function(i){
29829             i.el.setStyle('width', width + '%');
29830             i.topEl.el.setStyle('width', width + '%');
29831             i.bottomEl.el.setStyle('width', width + '%');
29832         }, this);
29833         
29834     }
29835     
29836 });
29837 /*
29838  * - LGPL
29839  *
29840  * Nav Progress Item
29841  * 
29842  */
29843
29844 /**
29845  * @class Roo.bootstrap.NavProgressItem
29846  * @extends Roo.bootstrap.Component
29847  * Bootstrap NavProgressItem class
29848  * @cfg {String} rid the reference id
29849  * @cfg {Boolean} active (true|false) Is item active default false
29850  * @cfg {Boolean} disabled (true|false) Is item active default false
29851  * @cfg {String} html
29852  * @cfg {String} position (top|bottom) text position default bottom
29853  * @cfg {String} icon show icon instead of number
29854  * 
29855  * @constructor
29856  * Create a new NavProgressItem
29857  * @param {Object} config The config object
29858  */
29859 Roo.bootstrap.NavProgressItem = function(config){
29860     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29861     this.addEvents({
29862         // raw events
29863         /**
29864          * @event click
29865          * The raw click event for the entire grid.
29866          * @param {Roo.bootstrap.NavProgressItem} this
29867          * @param {Roo.EventObject} e
29868          */
29869         "click" : true
29870     });
29871    
29872 };
29873
29874 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29875     
29876     rid : '',
29877     active : false,
29878     disabled : false,
29879     html : '',
29880     position : 'bottom',
29881     icon : false,
29882     
29883     getAutoCreate : function()
29884     {
29885         var iconCls = 'roo-navigation-bar-item-icon';
29886         
29887         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29888         
29889         var cfg = {
29890             tag: 'li',
29891             cls: 'roo-navigation-bar-item',
29892             cn : [
29893                 {
29894                     tag : 'i',
29895                     cls : iconCls
29896                 }
29897             ]
29898         };
29899         
29900         if(this.active){
29901             cfg.cls += ' active';
29902         }
29903         if(this.disabled){
29904             cfg.cls += ' disabled';
29905         }
29906         
29907         return cfg;
29908     },
29909     
29910     disable : function()
29911     {
29912         this.setDisabled(true);
29913     },
29914     
29915     enable : function()
29916     {
29917         this.setDisabled(false);
29918     },
29919     
29920     initEvents: function() 
29921     {
29922         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29923         
29924         this.iconEl.on('click', this.onClick, this);
29925     },
29926     
29927     onClick : function(e)
29928     {
29929         e.preventDefault();
29930         
29931         if(this.disabled){
29932             return;
29933         }
29934         
29935         if(this.fireEvent('click', this, e) === false){
29936             return;
29937         };
29938         
29939         this.parent().setActiveItem(this);
29940     },
29941     
29942     isActive: function () 
29943     {
29944         return this.active;
29945     },
29946     
29947     setActive : function(state)
29948     {
29949         if(this.active == state){
29950             return;
29951         }
29952         
29953         this.active = state;
29954         
29955         if (state) {
29956             this.el.addClass('active');
29957             return;
29958         }
29959         
29960         this.el.removeClass('active');
29961         
29962         return;
29963     },
29964     
29965     setDisabled : function(state)
29966     {
29967         if(this.disabled == state){
29968             return;
29969         }
29970         
29971         this.disabled = state;
29972         
29973         if (state) {
29974             this.el.addClass('disabled');
29975             return;
29976         }
29977         
29978         this.el.removeClass('disabled');
29979     },
29980     
29981     tooltipEl : function()
29982     {
29983         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29984     }
29985 });
29986  
29987
29988  /*
29989  * - LGPL
29990  *
29991  * FieldLabel
29992  * 
29993  */
29994
29995 /**
29996  * @class Roo.bootstrap.FieldLabel
29997  * @extends Roo.bootstrap.Component
29998  * Bootstrap FieldLabel class
29999  * @cfg {String} html contents of the element
30000  * @cfg {String} tag tag of the element default label
30001  * @cfg {String} cls class of the element
30002  * @cfg {String} target label target 
30003  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30004  * @cfg {String} invalidClass default "text-warning"
30005  * @cfg {String} validClass default "text-success"
30006  * @cfg {String} iconTooltip default "This field is required"
30007  * @cfg {String} indicatorpos (left|right) default left
30008  * 
30009  * @constructor
30010  * Create a new FieldLabel
30011  * @param {Object} config The config object
30012  */
30013
30014 Roo.bootstrap.FieldLabel = function(config){
30015     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30016     
30017     this.addEvents({
30018             /**
30019              * @event invalid
30020              * Fires after the field has been marked as invalid.
30021              * @param {Roo.form.FieldLabel} this
30022              * @param {String} msg The validation message
30023              */
30024             invalid : true,
30025             /**
30026              * @event valid
30027              * Fires after the field has been validated with no errors.
30028              * @param {Roo.form.FieldLabel} this
30029              */
30030             valid : true
30031         });
30032 };
30033
30034 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30035     
30036     tag: 'label',
30037     cls: '',
30038     html: '',
30039     target: '',
30040     allowBlank : true,
30041     invalidClass : 'has-warning',
30042     validClass : 'has-success',
30043     iconTooltip : 'This field is required',
30044     indicatorpos : 'left',
30045     
30046     getAutoCreate : function(){
30047         
30048         var cls = "";
30049         if (!this.allowBlank) {
30050             cls  = "visible";
30051         }
30052         
30053         var cfg = {
30054             tag : this.tag,
30055             cls : 'roo-bootstrap-field-label ' + this.cls,
30056             for : this.target,
30057             cn : [
30058                 {
30059                     tag : 'i',
30060                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30061                     tooltip : this.iconTooltip
30062                 },
30063                 {
30064                     tag : 'span',
30065                     html : this.html
30066                 }
30067             ] 
30068         };
30069         
30070         if(this.indicatorpos == 'right'){
30071             var cfg = {
30072                 tag : this.tag,
30073                 cls : 'roo-bootstrap-field-label ' + this.cls,
30074                 for : this.target,
30075                 cn : [
30076                     {
30077                         tag : 'span',
30078                         html : this.html
30079                     },
30080                     {
30081                         tag : 'i',
30082                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30083                         tooltip : this.iconTooltip
30084                     }
30085                 ] 
30086             };
30087         }
30088         
30089         return cfg;
30090     },
30091     
30092     initEvents: function() 
30093     {
30094         Roo.bootstrap.Element.superclass.initEvents.call(this);
30095         
30096         this.indicator = this.indicatorEl();
30097         
30098         if(this.indicator){
30099             this.indicator.removeClass('visible');
30100             this.indicator.addClass('invisible');
30101         }
30102         
30103         Roo.bootstrap.FieldLabel.register(this);
30104     },
30105     
30106     indicatorEl : function()
30107     {
30108         var indicator = this.el.select('i.roo-required-indicator',true).first();
30109         
30110         if(!indicator){
30111             return false;
30112         }
30113         
30114         return indicator;
30115         
30116     },
30117     
30118     /**
30119      * Mark this field as valid
30120      */
30121     markValid : function()
30122     {
30123         if(this.indicator){
30124             this.indicator.removeClass('visible');
30125             this.indicator.addClass('invisible');
30126         }
30127         
30128         this.el.removeClass(this.invalidClass);
30129         
30130         this.el.addClass(this.validClass);
30131         
30132         this.fireEvent('valid', this);
30133     },
30134     
30135     /**
30136      * Mark this field as invalid
30137      * @param {String} msg The validation message
30138      */
30139     markInvalid : function(msg)
30140     {
30141         if(this.indicator){
30142             this.indicator.removeClass('invisible');
30143             this.indicator.addClass('visible');
30144         }
30145         
30146         this.el.removeClass(this.validClass);
30147         
30148         this.el.addClass(this.invalidClass);
30149         
30150         this.fireEvent('invalid', this, msg);
30151     }
30152     
30153    
30154 });
30155
30156 Roo.apply(Roo.bootstrap.FieldLabel, {
30157     
30158     groups: {},
30159     
30160      /**
30161     * register a FieldLabel Group
30162     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30163     */
30164     register : function(label)
30165     {
30166         if(this.groups.hasOwnProperty(label.target)){
30167             return;
30168         }
30169      
30170         this.groups[label.target] = label;
30171         
30172     },
30173     /**
30174     * fetch a FieldLabel Group based on the target
30175     * @param {string} target
30176     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30177     */
30178     get: function(target) {
30179         if (typeof(this.groups[target]) == 'undefined') {
30180             return false;
30181         }
30182         
30183         return this.groups[target] ;
30184     }
30185 });
30186
30187  
30188
30189  /*
30190  * - LGPL
30191  *
30192  * page DateSplitField.
30193  * 
30194  */
30195
30196
30197 /**
30198  * @class Roo.bootstrap.DateSplitField
30199  * @extends Roo.bootstrap.Component
30200  * Bootstrap DateSplitField class
30201  * @cfg {string} fieldLabel - the label associated
30202  * @cfg {Number} labelWidth set the width of label (0-12)
30203  * @cfg {String} labelAlign (top|left)
30204  * @cfg {Boolean} dayAllowBlank (true|false) default false
30205  * @cfg {Boolean} monthAllowBlank (true|false) default false
30206  * @cfg {Boolean} yearAllowBlank (true|false) default false
30207  * @cfg {string} dayPlaceholder 
30208  * @cfg {string} monthPlaceholder
30209  * @cfg {string} yearPlaceholder
30210  * @cfg {string} dayFormat default 'd'
30211  * @cfg {string} monthFormat default 'm'
30212  * @cfg {string} yearFormat default 'Y'
30213  * @cfg {Number} labellg set the width of label (1-12)
30214  * @cfg {Number} labelmd set the width of label (1-12)
30215  * @cfg {Number} labelsm set the width of label (1-12)
30216  * @cfg {Number} labelxs set the width of label (1-12)
30217
30218  *     
30219  * @constructor
30220  * Create a new DateSplitField
30221  * @param {Object} config The config object
30222  */
30223
30224 Roo.bootstrap.DateSplitField = function(config){
30225     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30226     
30227     this.addEvents({
30228         // raw events
30229          /**
30230          * @event years
30231          * getting the data of years
30232          * @param {Roo.bootstrap.DateSplitField} this
30233          * @param {Object} years
30234          */
30235         "years" : true,
30236         /**
30237          * @event days
30238          * getting the data of days
30239          * @param {Roo.bootstrap.DateSplitField} this
30240          * @param {Object} days
30241          */
30242         "days" : true,
30243         /**
30244          * @event invalid
30245          * Fires after the field has been marked as invalid.
30246          * @param {Roo.form.Field} this
30247          * @param {String} msg The validation message
30248          */
30249         invalid : true,
30250        /**
30251          * @event valid
30252          * Fires after the field has been validated with no errors.
30253          * @param {Roo.form.Field} this
30254          */
30255         valid : true
30256     });
30257 };
30258
30259 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30260     
30261     fieldLabel : '',
30262     labelAlign : 'top',
30263     labelWidth : 3,
30264     dayAllowBlank : false,
30265     monthAllowBlank : false,
30266     yearAllowBlank : false,
30267     dayPlaceholder : '',
30268     monthPlaceholder : '',
30269     yearPlaceholder : '',
30270     dayFormat : 'd',
30271     monthFormat : 'm',
30272     yearFormat : 'Y',
30273     isFormField : true,
30274     labellg : 0,
30275     labelmd : 0,
30276     labelsm : 0,
30277     labelxs : 0,
30278     
30279     getAutoCreate : function()
30280     {
30281         var cfg = {
30282             tag : 'div',
30283             cls : 'row roo-date-split-field-group',
30284             cn : [
30285                 {
30286                     tag : 'input',
30287                     type : 'hidden',
30288                     cls : 'form-hidden-field roo-date-split-field-group-value',
30289                     name : this.name
30290                 }
30291             ]
30292         };
30293         
30294         var labelCls = 'col-md-12';
30295         var contentCls = 'col-md-4';
30296         
30297         if(this.fieldLabel){
30298             
30299             var label = {
30300                 tag : 'div',
30301                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30302                 cn : [
30303                     {
30304                         tag : 'label',
30305                         html : this.fieldLabel
30306                     }
30307                 ]
30308             };
30309             
30310             if(this.labelAlign == 'left'){
30311             
30312                 if(this.labelWidth > 12){
30313                     label.style = "width: " + this.labelWidth + 'px';
30314                 }
30315
30316                 if(this.labelWidth < 13 && this.labelmd == 0){
30317                     this.labelmd = this.labelWidth;
30318                 }
30319
30320                 if(this.labellg > 0){
30321                     labelCls = ' col-lg-' + this.labellg;
30322                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30323                 }
30324
30325                 if(this.labelmd > 0){
30326                     labelCls = ' col-md-' + this.labelmd;
30327                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30328                 }
30329
30330                 if(this.labelsm > 0){
30331                     labelCls = ' col-sm-' + this.labelsm;
30332                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30333                 }
30334
30335                 if(this.labelxs > 0){
30336                     labelCls = ' col-xs-' + this.labelxs;
30337                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30338                 }
30339             }
30340             
30341             label.cls += ' ' + labelCls;
30342             
30343             cfg.cn.push(label);
30344         }
30345         
30346         Roo.each(['day', 'month', 'year'], function(t){
30347             cfg.cn.push({
30348                 tag : 'div',
30349                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30350             });
30351         }, this);
30352         
30353         return cfg;
30354     },
30355     
30356     inputEl: function ()
30357     {
30358         return this.el.select('.roo-date-split-field-group-value', true).first();
30359     },
30360     
30361     onRender : function(ct, position) 
30362     {
30363         var _this = this;
30364         
30365         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30366         
30367         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30368         
30369         this.dayField = new Roo.bootstrap.ComboBox({
30370             allowBlank : this.dayAllowBlank,
30371             alwaysQuery : true,
30372             displayField : 'value',
30373             editable : false,
30374             fieldLabel : '',
30375             forceSelection : true,
30376             mode : 'local',
30377             placeholder : this.dayPlaceholder,
30378             selectOnFocus : true,
30379             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30380             triggerAction : 'all',
30381             typeAhead : true,
30382             valueField : 'value',
30383             store : new Roo.data.SimpleStore({
30384                 data : (function() {    
30385                     var days = [];
30386                     _this.fireEvent('days', _this, days);
30387                     return days;
30388                 })(),
30389                 fields : [ 'value' ]
30390             }),
30391             listeners : {
30392                 select : function (_self, record, index)
30393                 {
30394                     _this.setValue(_this.getValue());
30395                 }
30396             }
30397         });
30398
30399         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30400         
30401         this.monthField = new Roo.bootstrap.MonthField({
30402             after : '<i class=\"fa fa-calendar\"></i>',
30403             allowBlank : this.monthAllowBlank,
30404             placeholder : this.monthPlaceholder,
30405             readOnly : true,
30406             listeners : {
30407                 render : function (_self)
30408                 {
30409                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30410                         e.preventDefault();
30411                         _self.focus();
30412                     });
30413                 },
30414                 select : function (_self, oldvalue, newvalue)
30415                 {
30416                     _this.setValue(_this.getValue());
30417                 }
30418             }
30419         });
30420         
30421         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30422         
30423         this.yearField = new Roo.bootstrap.ComboBox({
30424             allowBlank : this.yearAllowBlank,
30425             alwaysQuery : true,
30426             displayField : 'value',
30427             editable : false,
30428             fieldLabel : '',
30429             forceSelection : true,
30430             mode : 'local',
30431             placeholder : this.yearPlaceholder,
30432             selectOnFocus : true,
30433             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30434             triggerAction : 'all',
30435             typeAhead : true,
30436             valueField : 'value',
30437             store : new Roo.data.SimpleStore({
30438                 data : (function() {
30439                     var years = [];
30440                     _this.fireEvent('years', _this, years);
30441                     return years;
30442                 })(),
30443                 fields : [ 'value' ]
30444             }),
30445             listeners : {
30446                 select : function (_self, record, index)
30447                 {
30448                     _this.setValue(_this.getValue());
30449                 }
30450             }
30451         });
30452
30453         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30454     },
30455     
30456     setValue : function(v, format)
30457     {
30458         this.inputEl.dom.value = v;
30459         
30460         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30461         
30462         var d = Date.parseDate(v, f);
30463         
30464         if(!d){
30465             this.validate();
30466             return;
30467         }
30468         
30469         this.setDay(d.format(this.dayFormat));
30470         this.setMonth(d.format(this.monthFormat));
30471         this.setYear(d.format(this.yearFormat));
30472         
30473         this.validate();
30474         
30475         return;
30476     },
30477     
30478     setDay : function(v)
30479     {
30480         this.dayField.setValue(v);
30481         this.inputEl.dom.value = this.getValue();
30482         this.validate();
30483         return;
30484     },
30485     
30486     setMonth : function(v)
30487     {
30488         this.monthField.setValue(v, true);
30489         this.inputEl.dom.value = this.getValue();
30490         this.validate();
30491         return;
30492     },
30493     
30494     setYear : function(v)
30495     {
30496         this.yearField.setValue(v);
30497         this.inputEl.dom.value = this.getValue();
30498         this.validate();
30499         return;
30500     },
30501     
30502     getDay : function()
30503     {
30504         return this.dayField.getValue();
30505     },
30506     
30507     getMonth : function()
30508     {
30509         return this.monthField.getValue();
30510     },
30511     
30512     getYear : function()
30513     {
30514         return this.yearField.getValue();
30515     },
30516     
30517     getValue : function()
30518     {
30519         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30520         
30521         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30522         
30523         return date;
30524     },
30525     
30526     reset : function()
30527     {
30528         this.setDay('');
30529         this.setMonth('');
30530         this.setYear('');
30531         this.inputEl.dom.value = '';
30532         this.validate();
30533         return;
30534     },
30535     
30536     validate : function()
30537     {
30538         var d = this.dayField.validate();
30539         var m = this.monthField.validate();
30540         var y = this.yearField.validate();
30541         
30542         var valid = true;
30543         
30544         if(
30545                 (!this.dayAllowBlank && !d) ||
30546                 (!this.monthAllowBlank && !m) ||
30547                 (!this.yearAllowBlank && !y)
30548         ){
30549             valid = false;
30550         }
30551         
30552         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30553             return valid;
30554         }
30555         
30556         if(valid){
30557             this.markValid();
30558             return valid;
30559         }
30560         
30561         this.markInvalid();
30562         
30563         return valid;
30564     },
30565     
30566     markValid : function()
30567     {
30568         
30569         var label = this.el.select('label', true).first();
30570         var icon = this.el.select('i.fa-star', true).first();
30571
30572         if(label && icon){
30573             icon.remove();
30574         }
30575         
30576         this.fireEvent('valid', this);
30577     },
30578     
30579      /**
30580      * Mark this field as invalid
30581      * @param {String} msg The validation message
30582      */
30583     markInvalid : function(msg)
30584     {
30585         
30586         var label = this.el.select('label', true).first();
30587         var icon = this.el.select('i.fa-star', true).first();
30588
30589         if(label && !icon){
30590             this.el.select('.roo-date-split-field-label', true).createChild({
30591                 tag : 'i',
30592                 cls : 'text-danger fa fa-lg fa-star',
30593                 tooltip : 'This field is required',
30594                 style : 'margin-right:5px;'
30595             }, label, true);
30596         }
30597         
30598         this.fireEvent('invalid', this, msg);
30599     },
30600     
30601     clearInvalid : function()
30602     {
30603         var label = this.el.select('label', true).first();
30604         var icon = this.el.select('i.fa-star', true).first();
30605
30606         if(label && icon){
30607             icon.remove();
30608         }
30609         
30610         this.fireEvent('valid', this);
30611     },
30612     
30613     getName: function()
30614     {
30615         return this.name;
30616     }
30617     
30618 });
30619
30620  /**
30621  *
30622  * This is based on 
30623  * http://masonry.desandro.com
30624  *
30625  * The idea is to render all the bricks based on vertical width...
30626  *
30627  * The original code extends 'outlayer' - we might need to use that....
30628  * 
30629  */
30630
30631
30632 /**
30633  * @class Roo.bootstrap.LayoutMasonry
30634  * @extends Roo.bootstrap.Component
30635  * Bootstrap Layout Masonry class
30636  * 
30637  * @constructor
30638  * Create a new Element
30639  * @param {Object} config The config object
30640  */
30641
30642 Roo.bootstrap.LayoutMasonry = function(config){
30643     
30644     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30645     
30646     this.bricks = [];
30647     
30648     Roo.bootstrap.LayoutMasonry.register(this);
30649     
30650     this.addEvents({
30651         // raw events
30652         /**
30653          * @event layout
30654          * Fire after layout the items
30655          * @param {Roo.bootstrap.LayoutMasonry} this
30656          * @param {Roo.EventObject} e
30657          */
30658         "layout" : true
30659     });
30660     
30661 };
30662
30663 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30664     
30665     /**
30666      * @cfg {Boolean} isLayoutInstant = no animation?
30667      */   
30668     isLayoutInstant : false, // needed?
30669    
30670     /**
30671      * @cfg {Number} boxWidth  width of the columns
30672      */   
30673     boxWidth : 450,
30674     
30675       /**
30676      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30677      */   
30678     boxHeight : 0,
30679     
30680     /**
30681      * @cfg {Number} padWidth padding below box..
30682      */   
30683     padWidth : 10, 
30684     
30685     /**
30686      * @cfg {Number} gutter gutter width..
30687      */   
30688     gutter : 10,
30689     
30690      /**
30691      * @cfg {Number} maxCols maximum number of columns
30692      */   
30693     
30694     maxCols: 0,
30695     
30696     /**
30697      * @cfg {Boolean} isAutoInitial defalut true
30698      */   
30699     isAutoInitial : true, 
30700     
30701     containerWidth: 0,
30702     
30703     /**
30704      * @cfg {Boolean} isHorizontal defalut false
30705      */   
30706     isHorizontal : false, 
30707
30708     currentSize : null,
30709     
30710     tag: 'div',
30711     
30712     cls: '',
30713     
30714     bricks: null, //CompositeElement
30715     
30716     cols : 1,
30717     
30718     _isLayoutInited : false,
30719     
30720 //    isAlternative : false, // only use for vertical layout...
30721     
30722     /**
30723      * @cfg {Number} alternativePadWidth padding below box..
30724      */   
30725     alternativePadWidth : 50,
30726     
30727     selectedBrick : [],
30728     
30729     getAutoCreate : function(){
30730         
30731         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30732         
30733         var cfg = {
30734             tag: this.tag,
30735             cls: 'blog-masonary-wrapper ' + this.cls,
30736             cn : {
30737                 cls : 'mas-boxes masonary'
30738             }
30739         };
30740         
30741         return cfg;
30742     },
30743     
30744     getChildContainer: function( )
30745     {
30746         if (this.boxesEl) {
30747             return this.boxesEl;
30748         }
30749         
30750         this.boxesEl = this.el.select('.mas-boxes').first();
30751         
30752         return this.boxesEl;
30753     },
30754     
30755     
30756     initEvents : function()
30757     {
30758         var _this = this;
30759         
30760         if(this.isAutoInitial){
30761             Roo.log('hook children rendered');
30762             this.on('childrenrendered', function() {
30763                 Roo.log('children rendered');
30764                 _this.initial();
30765             } ,this);
30766         }
30767     },
30768     
30769     initial : function()
30770     {
30771         this.selectedBrick = [];
30772         
30773         this.currentSize = this.el.getBox(true);
30774         
30775         Roo.EventManager.onWindowResize(this.resize, this); 
30776
30777         if(!this.isAutoInitial){
30778             this.layout();
30779             return;
30780         }
30781         
30782         this.layout();
30783         
30784         return;
30785         //this.layout.defer(500,this);
30786         
30787     },
30788     
30789     resize : function()
30790     {
30791         var cs = this.el.getBox(true);
30792         
30793         if (
30794                 this.currentSize.width == cs.width && 
30795                 this.currentSize.x == cs.x && 
30796                 this.currentSize.height == cs.height && 
30797                 this.currentSize.y == cs.y 
30798         ) {
30799             Roo.log("no change in with or X or Y");
30800             return;
30801         }
30802         
30803         this.currentSize = cs;
30804         
30805         this.layout();
30806         
30807     },
30808     
30809     layout : function()
30810     {   
30811         this._resetLayout();
30812         
30813         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30814         
30815         this.layoutItems( isInstant );
30816       
30817         this._isLayoutInited = true;
30818         
30819         this.fireEvent('layout', this);
30820         
30821     },
30822     
30823     _resetLayout : function()
30824     {
30825         if(this.isHorizontal){
30826             this.horizontalMeasureColumns();
30827             return;
30828         }
30829         
30830         this.verticalMeasureColumns();
30831         
30832     },
30833     
30834     verticalMeasureColumns : function()
30835     {
30836         this.getContainerWidth();
30837         
30838 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30839 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30840 //            return;
30841 //        }
30842         
30843         var boxWidth = this.boxWidth + this.padWidth;
30844         
30845         if(this.containerWidth < this.boxWidth){
30846             boxWidth = this.containerWidth
30847         }
30848         
30849         var containerWidth = this.containerWidth;
30850         
30851         var cols = Math.floor(containerWidth / boxWidth);
30852         
30853         this.cols = Math.max( cols, 1 );
30854         
30855         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30856         
30857         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30858         
30859         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30860         
30861         this.colWidth = boxWidth + avail - this.padWidth;
30862         
30863         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30864         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30865     },
30866     
30867     horizontalMeasureColumns : function()
30868     {
30869         this.getContainerWidth();
30870         
30871         var boxWidth = this.boxWidth;
30872         
30873         if(this.containerWidth < boxWidth){
30874             boxWidth = this.containerWidth;
30875         }
30876         
30877         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30878         
30879         this.el.setHeight(boxWidth);
30880         
30881     },
30882     
30883     getContainerWidth : function()
30884     {
30885         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30886     },
30887     
30888     layoutItems : function( isInstant )
30889     {
30890         Roo.log(this.bricks);
30891         
30892         var items = Roo.apply([], this.bricks);
30893         
30894         if(this.isHorizontal){
30895             this._horizontalLayoutItems( items , isInstant );
30896             return;
30897         }
30898         
30899 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30900 //            this._verticalAlternativeLayoutItems( items , isInstant );
30901 //            return;
30902 //        }
30903         
30904         this._verticalLayoutItems( items , isInstant );
30905         
30906     },
30907     
30908     _verticalLayoutItems : function ( items , isInstant)
30909     {
30910         if ( !items || !items.length ) {
30911             return;
30912         }
30913         
30914         var standard = [
30915             ['xs', 'xs', 'xs', 'tall'],
30916             ['xs', 'xs', 'tall'],
30917             ['xs', 'xs', 'sm'],
30918             ['xs', 'xs', 'xs'],
30919             ['xs', 'tall'],
30920             ['xs', 'sm'],
30921             ['xs', 'xs'],
30922             ['xs'],
30923             
30924             ['sm', 'xs', 'xs'],
30925             ['sm', 'xs'],
30926             ['sm'],
30927             
30928             ['tall', 'xs', 'xs', 'xs'],
30929             ['tall', 'xs', 'xs'],
30930             ['tall', 'xs'],
30931             ['tall']
30932             
30933         ];
30934         
30935         var queue = [];
30936         
30937         var boxes = [];
30938         
30939         var box = [];
30940         
30941         Roo.each(items, function(item, k){
30942             
30943             switch (item.size) {
30944                 // these layouts take up a full box,
30945                 case 'md' :
30946                 case 'md-left' :
30947                 case 'md-right' :
30948                 case 'wide' :
30949                     
30950                     if(box.length){
30951                         boxes.push(box);
30952                         box = [];
30953                     }
30954                     
30955                     boxes.push([item]);
30956                     
30957                     break;
30958                     
30959                 case 'xs' :
30960                 case 'sm' :
30961                 case 'tall' :
30962                     
30963                     box.push(item);
30964                     
30965                     break;
30966                 default :
30967                     break;
30968                     
30969             }
30970             
30971         }, this);
30972         
30973         if(box.length){
30974             boxes.push(box);
30975             box = [];
30976         }
30977         
30978         var filterPattern = function(box, length)
30979         {
30980             if(!box.length){
30981                 return;
30982             }
30983             
30984             var match = false;
30985             
30986             var pattern = box.slice(0, length);
30987             
30988             var format = [];
30989             
30990             Roo.each(pattern, function(i){
30991                 format.push(i.size);
30992             }, this);
30993             
30994             Roo.each(standard, function(s){
30995                 
30996                 if(String(s) != String(format)){
30997                     return;
30998                 }
30999                 
31000                 match = true;
31001                 return false;
31002                 
31003             }, this);
31004             
31005             if(!match && length == 1){
31006                 return;
31007             }
31008             
31009             if(!match){
31010                 filterPattern(box, length - 1);
31011                 return;
31012             }
31013                 
31014             queue.push(pattern);
31015
31016             box = box.slice(length, box.length);
31017
31018             filterPattern(box, 4);
31019
31020             return;
31021             
31022         }
31023         
31024         Roo.each(boxes, function(box, k){
31025             
31026             if(!box.length){
31027                 return;
31028             }
31029             
31030             if(box.length == 1){
31031                 queue.push(box);
31032                 return;
31033             }
31034             
31035             filterPattern(box, 4);
31036             
31037         }, this);
31038         
31039         this._processVerticalLayoutQueue( queue, isInstant );
31040         
31041     },
31042     
31043 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31044 //    {
31045 //        if ( !items || !items.length ) {
31046 //            return;
31047 //        }
31048 //
31049 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31050 //        
31051 //    },
31052     
31053     _horizontalLayoutItems : function ( items , isInstant)
31054     {
31055         if ( !items || !items.length || items.length < 3) {
31056             return;
31057         }
31058         
31059         items.reverse();
31060         
31061         var eItems = items.slice(0, 3);
31062         
31063         items = items.slice(3, items.length);
31064         
31065         var standard = [
31066             ['xs', 'xs', 'xs', 'wide'],
31067             ['xs', 'xs', 'wide'],
31068             ['xs', 'xs', 'sm'],
31069             ['xs', 'xs', 'xs'],
31070             ['xs', 'wide'],
31071             ['xs', 'sm'],
31072             ['xs', 'xs'],
31073             ['xs'],
31074             
31075             ['sm', 'xs', 'xs'],
31076             ['sm', 'xs'],
31077             ['sm'],
31078             
31079             ['wide', 'xs', 'xs', 'xs'],
31080             ['wide', 'xs', 'xs'],
31081             ['wide', 'xs'],
31082             ['wide'],
31083             
31084             ['wide-thin']
31085         ];
31086         
31087         var queue = [];
31088         
31089         var boxes = [];
31090         
31091         var box = [];
31092         
31093         Roo.each(items, function(item, k){
31094             
31095             switch (item.size) {
31096                 case 'md' :
31097                 case 'md-left' :
31098                 case 'md-right' :
31099                 case 'tall' :
31100                     
31101                     if(box.length){
31102                         boxes.push(box);
31103                         box = [];
31104                     }
31105                     
31106                     boxes.push([item]);
31107                     
31108                     break;
31109                     
31110                 case 'xs' :
31111                 case 'sm' :
31112                 case 'wide' :
31113                 case 'wide-thin' :
31114                     
31115                     box.push(item);
31116                     
31117                     break;
31118                 default :
31119                     break;
31120                     
31121             }
31122             
31123         }, this);
31124         
31125         if(box.length){
31126             boxes.push(box);
31127             box = [];
31128         }
31129         
31130         var filterPattern = function(box, length)
31131         {
31132             if(!box.length){
31133                 return;
31134             }
31135             
31136             var match = false;
31137             
31138             var pattern = box.slice(0, length);
31139             
31140             var format = [];
31141             
31142             Roo.each(pattern, function(i){
31143                 format.push(i.size);
31144             }, this);
31145             
31146             Roo.each(standard, function(s){
31147                 
31148                 if(String(s) != String(format)){
31149                     return;
31150                 }
31151                 
31152                 match = true;
31153                 return false;
31154                 
31155             }, this);
31156             
31157             if(!match && length == 1){
31158                 return;
31159             }
31160             
31161             if(!match){
31162                 filterPattern(box, length - 1);
31163                 return;
31164             }
31165                 
31166             queue.push(pattern);
31167
31168             box = box.slice(length, box.length);
31169
31170             filterPattern(box, 4);
31171
31172             return;
31173             
31174         }
31175         
31176         Roo.each(boxes, function(box, k){
31177             
31178             if(!box.length){
31179                 return;
31180             }
31181             
31182             if(box.length == 1){
31183                 queue.push(box);
31184                 return;
31185             }
31186             
31187             filterPattern(box, 4);
31188             
31189         }, this);
31190         
31191         
31192         var prune = [];
31193         
31194         var pos = this.el.getBox(true);
31195         
31196         var minX = pos.x;
31197         
31198         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31199         
31200         var hit_end = false;
31201         
31202         Roo.each(queue, function(box){
31203             
31204             if(hit_end){
31205                 
31206                 Roo.each(box, function(b){
31207                 
31208                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31209                     b.el.hide();
31210
31211                 }, this);
31212
31213                 return;
31214             }
31215             
31216             var mx = 0;
31217             
31218             Roo.each(box, function(b){
31219                 
31220                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31221                 b.el.show();
31222
31223                 mx = Math.max(mx, b.x);
31224                 
31225             }, this);
31226             
31227             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31228             
31229             if(maxX < minX){
31230                 
31231                 Roo.each(box, function(b){
31232                 
31233                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31234                     b.el.hide();
31235                     
31236                 }, this);
31237                 
31238                 hit_end = true;
31239                 
31240                 return;
31241             }
31242             
31243             prune.push(box);
31244             
31245         }, this);
31246         
31247         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31248     },
31249     
31250     /** Sets position of item in DOM
31251     * @param {Element} item
31252     * @param {Number} x - horizontal position
31253     * @param {Number} y - vertical position
31254     * @param {Boolean} isInstant - disables transitions
31255     */
31256     _processVerticalLayoutQueue : function( queue, isInstant )
31257     {
31258         var pos = this.el.getBox(true);
31259         var x = pos.x;
31260         var y = pos.y;
31261         var maxY = [];
31262         
31263         for (var i = 0; i < this.cols; i++){
31264             maxY[i] = pos.y;
31265         }
31266         
31267         Roo.each(queue, function(box, k){
31268             
31269             var col = k % this.cols;
31270             
31271             Roo.each(box, function(b,kk){
31272                 
31273                 b.el.position('absolute');
31274                 
31275                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31276                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31277                 
31278                 if(b.size == 'md-left' || b.size == 'md-right'){
31279                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31280                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31281                 }
31282                 
31283                 b.el.setWidth(width);
31284                 b.el.setHeight(height);
31285                 // iframe?
31286                 b.el.select('iframe',true).setSize(width,height);
31287                 
31288             }, this);
31289             
31290             for (var i = 0; i < this.cols; i++){
31291                 
31292                 if(maxY[i] < maxY[col]){
31293                     col = i;
31294                     continue;
31295                 }
31296                 
31297                 col = Math.min(col, i);
31298                 
31299             }
31300             
31301             x = pos.x + col * (this.colWidth + this.padWidth);
31302             
31303             y = maxY[col];
31304             
31305             var positions = [];
31306             
31307             switch (box.length){
31308                 case 1 :
31309                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31310                     break;
31311                 case 2 :
31312                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31313                     break;
31314                 case 3 :
31315                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31316                     break;
31317                 case 4 :
31318                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31319                     break;
31320                 default :
31321                     break;
31322             }
31323             
31324             Roo.each(box, function(b,kk){
31325                 
31326                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31327                 
31328                 var sz = b.el.getSize();
31329                 
31330                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31331                 
31332             }, this);
31333             
31334         }, this);
31335         
31336         var mY = 0;
31337         
31338         for (var i = 0; i < this.cols; i++){
31339             mY = Math.max(mY, maxY[i]);
31340         }
31341         
31342         this.el.setHeight(mY - pos.y);
31343         
31344     },
31345     
31346 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31347 //    {
31348 //        var pos = this.el.getBox(true);
31349 //        var x = pos.x;
31350 //        var y = pos.y;
31351 //        var maxX = pos.right;
31352 //        
31353 //        var maxHeight = 0;
31354 //        
31355 //        Roo.each(items, function(item, k){
31356 //            
31357 //            var c = k % 2;
31358 //            
31359 //            item.el.position('absolute');
31360 //                
31361 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31362 //
31363 //            item.el.setWidth(width);
31364 //
31365 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31366 //
31367 //            item.el.setHeight(height);
31368 //            
31369 //            if(c == 0){
31370 //                item.el.setXY([x, y], isInstant ? false : true);
31371 //            } else {
31372 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31373 //            }
31374 //            
31375 //            y = y + height + this.alternativePadWidth;
31376 //            
31377 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31378 //            
31379 //        }, this);
31380 //        
31381 //        this.el.setHeight(maxHeight);
31382 //        
31383 //    },
31384     
31385     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31386     {
31387         var pos = this.el.getBox(true);
31388         
31389         var minX = pos.x;
31390         var minY = pos.y;
31391         
31392         var maxX = pos.right;
31393         
31394         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31395         
31396         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31397         
31398         Roo.each(queue, function(box, k){
31399             
31400             Roo.each(box, function(b, kk){
31401                 
31402                 b.el.position('absolute');
31403                 
31404                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31405                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31406                 
31407                 if(b.size == 'md-left' || b.size == 'md-right'){
31408                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31409                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31410                 }
31411                 
31412                 b.el.setWidth(width);
31413                 b.el.setHeight(height);
31414                 
31415             }, this);
31416             
31417             if(!box.length){
31418                 return;
31419             }
31420             
31421             var positions = [];
31422             
31423             switch (box.length){
31424                 case 1 :
31425                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31426                     break;
31427                 case 2 :
31428                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31429                     break;
31430                 case 3 :
31431                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31432                     break;
31433                 case 4 :
31434                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31435                     break;
31436                 default :
31437                     break;
31438             }
31439             
31440             Roo.each(box, function(b,kk){
31441                 
31442                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31443                 
31444                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31445                 
31446             }, this);
31447             
31448         }, this);
31449         
31450     },
31451     
31452     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31453     {
31454         Roo.each(eItems, function(b,k){
31455             
31456             b.size = (k == 0) ? 'sm' : 'xs';
31457             b.x = (k == 0) ? 2 : 1;
31458             b.y = (k == 0) ? 2 : 1;
31459             
31460             b.el.position('absolute');
31461             
31462             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31463                 
31464             b.el.setWidth(width);
31465             
31466             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31467             
31468             b.el.setHeight(height);
31469             
31470         }, this);
31471
31472         var positions = [];
31473         
31474         positions.push({
31475             x : maxX - this.unitWidth * 2 - this.gutter,
31476             y : minY
31477         });
31478         
31479         positions.push({
31480             x : maxX - this.unitWidth,
31481             y : minY + (this.unitWidth + this.gutter) * 2
31482         });
31483         
31484         positions.push({
31485             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31486             y : minY
31487         });
31488         
31489         Roo.each(eItems, function(b,k){
31490             
31491             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31492
31493         }, this);
31494         
31495     },
31496     
31497     getVerticalOneBoxColPositions : function(x, y, box)
31498     {
31499         var pos = [];
31500         
31501         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31502         
31503         if(box[0].size == 'md-left'){
31504             rand = 0;
31505         }
31506         
31507         if(box[0].size == 'md-right'){
31508             rand = 1;
31509         }
31510         
31511         pos.push({
31512             x : x + (this.unitWidth + this.gutter) * rand,
31513             y : y
31514         });
31515         
31516         return pos;
31517     },
31518     
31519     getVerticalTwoBoxColPositions : function(x, y, box)
31520     {
31521         var pos = [];
31522         
31523         if(box[0].size == 'xs'){
31524             
31525             pos.push({
31526                 x : x,
31527                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31528             });
31529
31530             pos.push({
31531                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31532                 y : y
31533             });
31534             
31535             return pos;
31536             
31537         }
31538         
31539         pos.push({
31540             x : x,
31541             y : y
31542         });
31543
31544         pos.push({
31545             x : x + (this.unitWidth + this.gutter) * 2,
31546             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31547         });
31548         
31549         return pos;
31550         
31551     },
31552     
31553     getVerticalThreeBoxColPositions : function(x, y, box)
31554     {
31555         var pos = [];
31556         
31557         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31558             
31559             pos.push({
31560                 x : x,
31561                 y : y
31562             });
31563
31564             pos.push({
31565                 x : x + (this.unitWidth + this.gutter) * 1,
31566                 y : y
31567             });
31568             
31569             pos.push({
31570                 x : x + (this.unitWidth + this.gutter) * 2,
31571                 y : y
31572             });
31573             
31574             return pos;
31575             
31576         }
31577         
31578         if(box[0].size == 'xs' && box[1].size == 'xs'){
31579             
31580             pos.push({
31581                 x : x,
31582                 y : y
31583             });
31584
31585             pos.push({
31586                 x : x,
31587                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31588             });
31589             
31590             pos.push({
31591                 x : x + (this.unitWidth + this.gutter) * 1,
31592                 y : y
31593             });
31594             
31595             return pos;
31596             
31597         }
31598         
31599         pos.push({
31600             x : x,
31601             y : y
31602         });
31603
31604         pos.push({
31605             x : x + (this.unitWidth + this.gutter) * 2,
31606             y : y
31607         });
31608
31609         pos.push({
31610             x : x + (this.unitWidth + this.gutter) * 2,
31611             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31612         });
31613             
31614         return pos;
31615         
31616     },
31617     
31618     getVerticalFourBoxColPositions : function(x, y, box)
31619     {
31620         var pos = [];
31621         
31622         if(box[0].size == 'xs'){
31623             
31624             pos.push({
31625                 x : x,
31626                 y : y
31627             });
31628
31629             pos.push({
31630                 x : x,
31631                 y : y + (this.unitHeight + this.gutter) * 1
31632             });
31633             
31634             pos.push({
31635                 x : x,
31636                 y : y + (this.unitHeight + this.gutter) * 2
31637             });
31638             
31639             pos.push({
31640                 x : x + (this.unitWidth + this.gutter) * 1,
31641                 y : y
31642             });
31643             
31644             return pos;
31645             
31646         }
31647         
31648         pos.push({
31649             x : x,
31650             y : y
31651         });
31652
31653         pos.push({
31654             x : x + (this.unitWidth + this.gutter) * 2,
31655             y : y
31656         });
31657
31658         pos.push({
31659             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31660             y : y + (this.unitHeight + this.gutter) * 1
31661         });
31662
31663         pos.push({
31664             x : x + (this.unitWidth + this.gutter) * 2,
31665             y : y + (this.unitWidth + this.gutter) * 2
31666         });
31667
31668         return pos;
31669         
31670     },
31671     
31672     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31673     {
31674         var pos = [];
31675         
31676         if(box[0].size == 'md-left'){
31677             pos.push({
31678                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31679                 y : minY
31680             });
31681             
31682             return pos;
31683         }
31684         
31685         if(box[0].size == 'md-right'){
31686             pos.push({
31687                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31688                 y : minY + (this.unitWidth + this.gutter) * 1
31689             });
31690             
31691             return pos;
31692         }
31693         
31694         var rand = Math.floor(Math.random() * (4 - box[0].y));
31695         
31696         pos.push({
31697             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31698             y : minY + (this.unitWidth + this.gutter) * rand
31699         });
31700         
31701         return pos;
31702         
31703     },
31704     
31705     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31706     {
31707         var pos = [];
31708         
31709         if(box[0].size == 'xs'){
31710             
31711             pos.push({
31712                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31713                 y : minY
31714             });
31715
31716             pos.push({
31717                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31718                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31719             });
31720             
31721             return pos;
31722             
31723         }
31724         
31725         pos.push({
31726             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31727             y : minY
31728         });
31729
31730         pos.push({
31731             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31732             y : minY + (this.unitWidth + this.gutter) * 2
31733         });
31734         
31735         return pos;
31736         
31737     },
31738     
31739     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31740     {
31741         var pos = [];
31742         
31743         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31744             
31745             pos.push({
31746                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31747                 y : minY
31748             });
31749
31750             pos.push({
31751                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31752                 y : minY + (this.unitWidth + this.gutter) * 1
31753             });
31754             
31755             pos.push({
31756                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31757                 y : minY + (this.unitWidth + this.gutter) * 2
31758             });
31759             
31760             return pos;
31761             
31762         }
31763         
31764         if(box[0].size == 'xs' && box[1].size == 'xs'){
31765             
31766             pos.push({
31767                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31768                 y : minY
31769             });
31770
31771             pos.push({
31772                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31773                 y : minY
31774             });
31775             
31776             pos.push({
31777                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31778                 y : minY + (this.unitWidth + this.gutter) * 1
31779             });
31780             
31781             return pos;
31782             
31783         }
31784         
31785         pos.push({
31786             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31787             y : minY
31788         });
31789
31790         pos.push({
31791             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31792             y : minY + (this.unitWidth + this.gutter) * 2
31793         });
31794
31795         pos.push({
31796             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31797             y : minY + (this.unitWidth + this.gutter) * 2
31798         });
31799             
31800         return pos;
31801         
31802     },
31803     
31804     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31805     {
31806         var pos = [];
31807         
31808         if(box[0].size == 'xs'){
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[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31817                 y : minY
31818             });
31819             
31820             pos.push({
31821                 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),
31822                 y : minY
31823             });
31824             
31825             pos.push({
31826                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31827                 y : minY + (this.unitWidth + this.gutter) * 1
31828             });
31829             
31830             return pos;
31831             
31832         }
31833         
31834         pos.push({
31835             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31836             y : minY
31837         });
31838         
31839         pos.push({
31840             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31841             y : minY + (this.unitWidth + this.gutter) * 2
31842         });
31843         
31844         pos.push({
31845             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31846             y : minY + (this.unitWidth + this.gutter) * 2
31847         });
31848         
31849         pos.push({
31850             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),
31851             y : minY + (this.unitWidth + this.gutter) * 2
31852         });
31853
31854         return pos;
31855         
31856     },
31857     
31858     /**
31859     * remove a Masonry Brick
31860     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31861     */
31862     removeBrick : function(brick_id)
31863     {
31864         if (!brick_id) {
31865             return;
31866         }
31867         
31868         for (var i = 0; i<this.bricks.length; i++) {
31869             if (this.bricks[i].id == brick_id) {
31870                 this.bricks.splice(i,1);
31871                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31872                 this.initial();
31873             }
31874         }
31875     },
31876     
31877     /**
31878     * adds a Masonry Brick
31879     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31880     */
31881     addBrick : function(cfg)
31882     {
31883         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31884         //this.register(cn);
31885         cn.parentId = this.id;
31886         cn.onRender(this.el, null);
31887         return cn;
31888     },
31889     
31890     /**
31891     * register a Masonry Brick
31892     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31893     */
31894     
31895     register : function(brick)
31896     {
31897         this.bricks.push(brick);
31898         brick.masonryId = this.id;
31899     },
31900     
31901     /**
31902     * clear all the Masonry Brick
31903     */
31904     clearAll : function()
31905     {
31906         this.bricks = [];
31907         //this.getChildContainer().dom.innerHTML = "";
31908         this.el.dom.innerHTML = '';
31909     },
31910     
31911     getSelected : function()
31912     {
31913         if (!this.selectedBrick) {
31914             return false;
31915         }
31916         
31917         return this.selectedBrick;
31918     }
31919 });
31920
31921 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31922     
31923     groups: {},
31924      /**
31925     * register a Masonry Layout
31926     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31927     */
31928     
31929     register : function(layout)
31930     {
31931         this.groups[layout.id] = layout;
31932     },
31933     /**
31934     * fetch a  Masonry Layout based on the masonry layout ID
31935     * @param {string} the masonry layout to add
31936     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31937     */
31938     
31939     get: function(layout_id) {
31940         if (typeof(this.groups[layout_id]) == 'undefined') {
31941             return false;
31942         }
31943         return this.groups[layout_id] ;
31944     }
31945     
31946     
31947     
31948 });
31949
31950  
31951
31952  /**
31953  *
31954  * This is based on 
31955  * http://masonry.desandro.com
31956  *
31957  * The idea is to render all the bricks based on vertical width...
31958  *
31959  * The original code extends 'outlayer' - we might need to use that....
31960  * 
31961  */
31962
31963
31964 /**
31965  * @class Roo.bootstrap.LayoutMasonryAuto
31966  * @extends Roo.bootstrap.Component
31967  * Bootstrap Layout Masonry class
31968  * 
31969  * @constructor
31970  * Create a new Element
31971  * @param {Object} config The config object
31972  */
31973
31974 Roo.bootstrap.LayoutMasonryAuto = function(config){
31975     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31976 };
31977
31978 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31979     
31980       /**
31981      * @cfg {Boolean} isFitWidth  - resize the width..
31982      */   
31983     isFitWidth : false,  // options..
31984     /**
31985      * @cfg {Boolean} isOriginLeft = left align?
31986      */   
31987     isOriginLeft : true,
31988     /**
31989      * @cfg {Boolean} isOriginTop = top align?
31990      */   
31991     isOriginTop : false,
31992     /**
31993      * @cfg {Boolean} isLayoutInstant = no animation?
31994      */   
31995     isLayoutInstant : false, // needed?
31996     /**
31997      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31998      */   
31999     isResizingContainer : true,
32000     /**
32001      * @cfg {Number} columnWidth  width of the columns 
32002      */   
32003     
32004     columnWidth : 0,
32005     
32006     /**
32007      * @cfg {Number} maxCols maximum number of columns
32008      */   
32009     
32010     maxCols: 0,
32011     /**
32012      * @cfg {Number} padHeight padding below box..
32013      */   
32014     
32015     padHeight : 10, 
32016     
32017     /**
32018      * @cfg {Boolean} isAutoInitial defalut true
32019      */   
32020     
32021     isAutoInitial : true, 
32022     
32023     // private?
32024     gutter : 0,
32025     
32026     containerWidth: 0,
32027     initialColumnWidth : 0,
32028     currentSize : null,
32029     
32030     colYs : null, // array.
32031     maxY : 0,
32032     padWidth: 10,
32033     
32034     
32035     tag: 'div',
32036     cls: '',
32037     bricks: null, //CompositeElement
32038     cols : 0, // array?
32039     // element : null, // wrapped now this.el
32040     _isLayoutInited : null, 
32041     
32042     
32043     getAutoCreate : function(){
32044         
32045         var cfg = {
32046             tag: this.tag,
32047             cls: 'blog-masonary-wrapper ' + this.cls,
32048             cn : {
32049                 cls : 'mas-boxes masonary'
32050             }
32051         };
32052         
32053         return cfg;
32054     },
32055     
32056     getChildContainer: function( )
32057     {
32058         if (this.boxesEl) {
32059             return this.boxesEl;
32060         }
32061         
32062         this.boxesEl = this.el.select('.mas-boxes').first();
32063         
32064         return this.boxesEl;
32065     },
32066     
32067     
32068     initEvents : function()
32069     {
32070         var _this = this;
32071         
32072         if(this.isAutoInitial){
32073             Roo.log('hook children rendered');
32074             this.on('childrenrendered', function() {
32075                 Roo.log('children rendered');
32076                 _this.initial();
32077             } ,this);
32078         }
32079         
32080     },
32081     
32082     initial : function()
32083     {
32084         this.reloadItems();
32085
32086         this.currentSize = this.el.getBox(true);
32087
32088         /// was window resize... - let's see if this works..
32089         Roo.EventManager.onWindowResize(this.resize, this); 
32090
32091         if(!this.isAutoInitial){
32092             this.layout();
32093             return;
32094         }
32095         
32096         this.layout.defer(500,this);
32097     },
32098     
32099     reloadItems: function()
32100     {
32101         this.bricks = this.el.select('.masonry-brick', true);
32102         
32103         this.bricks.each(function(b) {
32104             //Roo.log(b.getSize());
32105             if (!b.attr('originalwidth')) {
32106                 b.attr('originalwidth',  b.getSize().width);
32107             }
32108             
32109         });
32110         
32111         Roo.log(this.bricks.elements.length);
32112     },
32113     
32114     resize : function()
32115     {
32116         Roo.log('resize');
32117         var cs = this.el.getBox(true);
32118         
32119         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32120             Roo.log("no change in with or X");
32121             return;
32122         }
32123         this.currentSize = cs;
32124         this.layout();
32125     },
32126     
32127     layout : function()
32128     {
32129          Roo.log('layout');
32130         this._resetLayout();
32131         //this._manageStamps();
32132       
32133         // don't animate first layout
32134         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32135         this.layoutItems( isInstant );
32136       
32137         // flag for initalized
32138         this._isLayoutInited = true;
32139     },
32140     
32141     layoutItems : function( isInstant )
32142     {
32143         //var items = this._getItemsForLayout( this.items );
32144         // original code supports filtering layout items.. we just ignore it..
32145         
32146         this._layoutItems( this.bricks , isInstant );
32147       
32148         this._postLayout();
32149     },
32150     _layoutItems : function ( items , isInstant)
32151     {
32152        //this.fireEvent( 'layout', this, items );
32153     
32154
32155         if ( !items || !items.elements.length ) {
32156           // no items, emit event with empty array
32157             return;
32158         }
32159
32160         var queue = [];
32161         items.each(function(item) {
32162             Roo.log("layout item");
32163             Roo.log(item);
32164             // get x/y object from method
32165             var position = this._getItemLayoutPosition( item );
32166             // enqueue
32167             position.item = item;
32168             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32169             queue.push( position );
32170         }, this);
32171       
32172         this._processLayoutQueue( queue );
32173     },
32174     /** Sets position of item in DOM
32175     * @param {Element} item
32176     * @param {Number} x - horizontal position
32177     * @param {Number} y - vertical position
32178     * @param {Boolean} isInstant - disables transitions
32179     */
32180     _processLayoutQueue : function( queue )
32181     {
32182         for ( var i=0, len = queue.length; i < len; i++ ) {
32183             var obj = queue[i];
32184             obj.item.position('absolute');
32185             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32186         }
32187     },
32188       
32189     
32190     /**
32191     * Any logic you want to do after each layout,
32192     * i.e. size the container
32193     */
32194     _postLayout : function()
32195     {
32196         this.resizeContainer();
32197     },
32198     
32199     resizeContainer : function()
32200     {
32201         if ( !this.isResizingContainer ) {
32202             return;
32203         }
32204         var size = this._getContainerSize();
32205         if ( size ) {
32206             this.el.setSize(size.width,size.height);
32207             this.boxesEl.setSize(size.width,size.height);
32208         }
32209     },
32210     
32211     
32212     
32213     _resetLayout : function()
32214     {
32215         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32216         this.colWidth = this.el.getWidth();
32217         //this.gutter = this.el.getWidth(); 
32218         
32219         this.measureColumns();
32220
32221         // reset column Y
32222         var i = this.cols;
32223         this.colYs = [];
32224         while (i--) {
32225             this.colYs.push( 0 );
32226         }
32227     
32228         this.maxY = 0;
32229     },
32230
32231     measureColumns : function()
32232     {
32233         this.getContainerWidth();
32234       // if columnWidth is 0, default to outerWidth of first item
32235         if ( !this.columnWidth ) {
32236             var firstItem = this.bricks.first();
32237             Roo.log(firstItem);
32238             this.columnWidth  = this.containerWidth;
32239             if (firstItem && firstItem.attr('originalwidth') ) {
32240                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32241             }
32242             // columnWidth fall back to item of first element
32243             Roo.log("set column width?");
32244                         this.initialColumnWidth = this.columnWidth  ;
32245
32246             // if first elem has no width, default to size of container
32247             
32248         }
32249         
32250         
32251         if (this.initialColumnWidth) {
32252             this.columnWidth = this.initialColumnWidth;
32253         }
32254         
32255         
32256             
32257         // column width is fixed at the top - however if container width get's smaller we should
32258         // reduce it...
32259         
32260         // this bit calcs how man columns..
32261             
32262         var columnWidth = this.columnWidth += this.gutter;
32263       
32264         // calculate columns
32265         var containerWidth = this.containerWidth + this.gutter;
32266         
32267         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32268         // fix rounding errors, typically with gutters
32269         var excess = columnWidth - containerWidth % columnWidth;
32270         
32271         
32272         // if overshoot is less than a pixel, round up, otherwise floor it
32273         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32274         cols = Math[ mathMethod ]( cols );
32275         this.cols = Math.max( cols, 1 );
32276         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32277         
32278          // padding positioning..
32279         var totalColWidth = this.cols * this.columnWidth;
32280         var padavail = this.containerWidth - totalColWidth;
32281         // so for 2 columns - we need 3 'pads'
32282         
32283         var padNeeded = (1+this.cols) * this.padWidth;
32284         
32285         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32286         
32287         this.columnWidth += padExtra
32288         //this.padWidth = Math.floor(padavail /  ( this.cols));
32289         
32290         // adjust colum width so that padding is fixed??
32291         
32292         // we have 3 columns ... total = width * 3
32293         // we have X left over... that should be used by 
32294         
32295         //if (this.expandC) {
32296             
32297         //}
32298         
32299         
32300         
32301     },
32302     
32303     getContainerWidth : function()
32304     {
32305        /* // container is parent if fit width
32306         var container = this.isFitWidth ? this.element.parentNode : this.element;
32307         // check that this.size and size are there
32308         // IE8 triggers resize on body size change, so they might not be
32309         
32310         var size = getSize( container );  //FIXME
32311         this.containerWidth = size && size.innerWidth; //FIXME
32312         */
32313          
32314         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32315         
32316     },
32317     
32318     _getItemLayoutPosition : function( item )  // what is item?
32319     {
32320         // we resize the item to our columnWidth..
32321       
32322         item.setWidth(this.columnWidth);
32323         item.autoBoxAdjust  = false;
32324         
32325         var sz = item.getSize();
32326  
32327         // how many columns does this brick span
32328         var remainder = this.containerWidth % this.columnWidth;
32329         
32330         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32331         // round if off by 1 pixel, otherwise use ceil
32332         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32333         colSpan = Math.min( colSpan, this.cols );
32334         
32335         // normally this should be '1' as we dont' currently allow multi width columns..
32336         
32337         var colGroup = this._getColGroup( colSpan );
32338         // get the minimum Y value from the columns
32339         var minimumY = Math.min.apply( Math, colGroup );
32340         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32341         
32342         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32343          
32344         // position the brick
32345         var position = {
32346             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32347             y: this.currentSize.y + minimumY + this.padHeight
32348         };
32349         
32350         Roo.log(position);
32351         // apply setHeight to necessary columns
32352         var setHeight = minimumY + sz.height + this.padHeight;
32353         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32354         
32355         var setSpan = this.cols + 1 - colGroup.length;
32356         for ( var i = 0; i < setSpan; i++ ) {
32357           this.colYs[ shortColIndex + i ] = setHeight ;
32358         }
32359       
32360         return position;
32361     },
32362     
32363     /**
32364      * @param {Number} colSpan - number of columns the element spans
32365      * @returns {Array} colGroup
32366      */
32367     _getColGroup : function( colSpan )
32368     {
32369         if ( colSpan < 2 ) {
32370           // if brick spans only one column, use all the column Ys
32371           return this.colYs;
32372         }
32373       
32374         var colGroup = [];
32375         // how many different places could this brick fit horizontally
32376         var groupCount = this.cols + 1 - colSpan;
32377         // for each group potential horizontal position
32378         for ( var i = 0; i < groupCount; i++ ) {
32379           // make an array of colY values for that one group
32380           var groupColYs = this.colYs.slice( i, i + colSpan );
32381           // and get the max value of the array
32382           colGroup[i] = Math.max.apply( Math, groupColYs );
32383         }
32384         return colGroup;
32385     },
32386     /*
32387     _manageStamp : function( stamp )
32388     {
32389         var stampSize =  stamp.getSize();
32390         var offset = stamp.getBox();
32391         // get the columns that this stamp affects
32392         var firstX = this.isOriginLeft ? offset.x : offset.right;
32393         var lastX = firstX + stampSize.width;
32394         var firstCol = Math.floor( firstX / this.columnWidth );
32395         firstCol = Math.max( 0, firstCol );
32396         
32397         var lastCol = Math.floor( lastX / this.columnWidth );
32398         // lastCol should not go over if multiple of columnWidth #425
32399         lastCol -= lastX % this.columnWidth ? 0 : 1;
32400         lastCol = Math.min( this.cols - 1, lastCol );
32401         
32402         // set colYs to bottom of the stamp
32403         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32404             stampSize.height;
32405             
32406         for ( var i = firstCol; i <= lastCol; i++ ) {
32407           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32408         }
32409     },
32410     */
32411     
32412     _getContainerSize : function()
32413     {
32414         this.maxY = Math.max.apply( Math, this.colYs );
32415         var size = {
32416             height: this.maxY
32417         };
32418       
32419         if ( this.isFitWidth ) {
32420             size.width = this._getContainerFitWidth();
32421         }
32422       
32423         return size;
32424     },
32425     
32426     _getContainerFitWidth : function()
32427     {
32428         var unusedCols = 0;
32429         // count unused columns
32430         var i = this.cols;
32431         while ( --i ) {
32432           if ( this.colYs[i] !== 0 ) {
32433             break;
32434           }
32435           unusedCols++;
32436         }
32437         // fit container to columns that have been used
32438         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32439     },
32440     
32441     needsResizeLayout : function()
32442     {
32443         var previousWidth = this.containerWidth;
32444         this.getContainerWidth();
32445         return previousWidth !== this.containerWidth;
32446     }
32447  
32448 });
32449
32450  
32451
32452  /*
32453  * - LGPL
32454  *
32455  * element
32456  * 
32457  */
32458
32459 /**
32460  * @class Roo.bootstrap.MasonryBrick
32461  * @extends Roo.bootstrap.Component
32462  * Bootstrap MasonryBrick class
32463  * 
32464  * @constructor
32465  * Create a new MasonryBrick
32466  * @param {Object} config The config object
32467  */
32468
32469 Roo.bootstrap.MasonryBrick = function(config){
32470     
32471     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32472     
32473     Roo.bootstrap.MasonryBrick.register(this);
32474     
32475     this.addEvents({
32476         // raw events
32477         /**
32478          * @event click
32479          * When a MasonryBrick is clcik
32480          * @param {Roo.bootstrap.MasonryBrick} this
32481          * @param {Roo.EventObject} e
32482          */
32483         "click" : true
32484     });
32485 };
32486
32487 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32488     
32489     /**
32490      * @cfg {String} title
32491      */   
32492     title : '',
32493     /**
32494      * @cfg {String} html
32495      */   
32496     html : '',
32497     /**
32498      * @cfg {String} bgimage
32499      */   
32500     bgimage : '',
32501     /**
32502      * @cfg {String} videourl
32503      */   
32504     videourl : '',
32505     /**
32506      * @cfg {String} cls
32507      */   
32508     cls : '',
32509     /**
32510      * @cfg {String} href
32511      */   
32512     href : '',
32513     /**
32514      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32515      */   
32516     size : 'xs',
32517     
32518     /**
32519      * @cfg {String} placetitle (center|bottom)
32520      */   
32521     placetitle : '',
32522     
32523     /**
32524      * @cfg {Boolean} isFitContainer defalut true
32525      */   
32526     isFitContainer : true, 
32527     
32528     /**
32529      * @cfg {Boolean} preventDefault defalut false
32530      */   
32531     preventDefault : false, 
32532     
32533     /**
32534      * @cfg {Boolean} inverse defalut false
32535      */   
32536     maskInverse : false, 
32537     
32538     getAutoCreate : function()
32539     {
32540         if(!this.isFitContainer){
32541             return this.getSplitAutoCreate();
32542         }
32543         
32544         var cls = 'masonry-brick masonry-brick-full';
32545         
32546         if(this.href.length){
32547             cls += ' masonry-brick-link';
32548         }
32549         
32550         if(this.bgimage.length){
32551             cls += ' masonry-brick-image';
32552         }
32553         
32554         if(this.maskInverse){
32555             cls += ' mask-inverse';
32556         }
32557         
32558         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32559             cls += ' enable-mask';
32560         }
32561         
32562         if(this.size){
32563             cls += ' masonry-' + this.size + '-brick';
32564         }
32565         
32566         if(this.placetitle.length){
32567             
32568             switch (this.placetitle) {
32569                 case 'center' :
32570                     cls += ' masonry-center-title';
32571                     break;
32572                 case 'bottom' :
32573                     cls += ' masonry-bottom-title';
32574                     break;
32575                 default:
32576                     break;
32577             }
32578             
32579         } else {
32580             if(!this.html.length && !this.bgimage.length){
32581                 cls += ' masonry-center-title';
32582             }
32583
32584             if(!this.html.length && this.bgimage.length){
32585                 cls += ' masonry-bottom-title';
32586             }
32587         }
32588         
32589         if(this.cls){
32590             cls += ' ' + this.cls;
32591         }
32592         
32593         var cfg = {
32594             tag: (this.href.length) ? 'a' : 'div',
32595             cls: cls,
32596             cn: [
32597                 {
32598                     tag: 'div',
32599                     cls: 'masonry-brick-mask'
32600                 },
32601                 {
32602                     tag: 'div',
32603                     cls: 'masonry-brick-paragraph',
32604                     cn: []
32605                 }
32606             ]
32607         };
32608         
32609         if(this.href.length){
32610             cfg.href = this.href;
32611         }
32612         
32613         var cn = cfg.cn[1].cn;
32614         
32615         if(this.title.length){
32616             cn.push({
32617                 tag: 'h4',
32618                 cls: 'masonry-brick-title',
32619                 html: this.title
32620             });
32621         }
32622         
32623         if(this.html.length){
32624             cn.push({
32625                 tag: 'p',
32626                 cls: 'masonry-brick-text',
32627                 html: this.html
32628             });
32629         }
32630         
32631         if (!this.title.length && !this.html.length) {
32632             cfg.cn[1].cls += ' hide';
32633         }
32634         
32635         if(this.bgimage.length){
32636             cfg.cn.push({
32637                 tag: 'img',
32638                 cls: 'masonry-brick-image-view',
32639                 src: this.bgimage
32640             });
32641         }
32642         
32643         if(this.videourl.length){
32644             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32645             // youtube support only?
32646             cfg.cn.push({
32647                 tag: 'iframe',
32648                 cls: 'masonry-brick-image-view',
32649                 src: vurl,
32650                 frameborder : 0,
32651                 allowfullscreen : true
32652             });
32653         }
32654         
32655         return cfg;
32656         
32657     },
32658     
32659     getSplitAutoCreate : function()
32660     {
32661         var cls = 'masonry-brick masonry-brick-split';
32662         
32663         if(this.href.length){
32664             cls += ' masonry-brick-link';
32665         }
32666         
32667         if(this.bgimage.length){
32668             cls += ' masonry-brick-image';
32669         }
32670         
32671         if(this.size){
32672             cls += ' masonry-' + this.size + '-brick';
32673         }
32674         
32675         switch (this.placetitle) {
32676             case 'center' :
32677                 cls += ' masonry-center-title';
32678                 break;
32679             case 'bottom' :
32680                 cls += ' masonry-bottom-title';
32681                 break;
32682             default:
32683                 if(!this.bgimage.length){
32684                     cls += ' masonry-center-title';
32685                 }
32686
32687                 if(this.bgimage.length){
32688                     cls += ' masonry-bottom-title';
32689                 }
32690                 break;
32691         }
32692         
32693         if(this.cls){
32694             cls += ' ' + this.cls;
32695         }
32696         
32697         var cfg = {
32698             tag: (this.href.length) ? 'a' : 'div',
32699             cls: cls,
32700             cn: [
32701                 {
32702                     tag: 'div',
32703                     cls: 'masonry-brick-split-head',
32704                     cn: [
32705                         {
32706                             tag: 'div',
32707                             cls: 'masonry-brick-paragraph',
32708                             cn: []
32709                         }
32710                     ]
32711                 },
32712                 {
32713                     tag: 'div',
32714                     cls: 'masonry-brick-split-body',
32715                     cn: []
32716                 }
32717             ]
32718         };
32719         
32720         if(this.href.length){
32721             cfg.href = this.href;
32722         }
32723         
32724         if(this.title.length){
32725             cfg.cn[0].cn[0].cn.push({
32726                 tag: 'h4',
32727                 cls: 'masonry-brick-title',
32728                 html: this.title
32729             });
32730         }
32731         
32732         if(this.html.length){
32733             cfg.cn[1].cn.push({
32734                 tag: 'p',
32735                 cls: 'masonry-brick-text',
32736                 html: this.html
32737             });
32738         }
32739
32740         if(this.bgimage.length){
32741             cfg.cn[0].cn.push({
32742                 tag: 'img',
32743                 cls: 'masonry-brick-image-view',
32744                 src: this.bgimage
32745             });
32746         }
32747         
32748         if(this.videourl.length){
32749             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32750             // youtube support only?
32751             cfg.cn[0].cn.cn.push({
32752                 tag: 'iframe',
32753                 cls: 'masonry-brick-image-view',
32754                 src: vurl,
32755                 frameborder : 0,
32756                 allowfullscreen : true
32757             });
32758         }
32759         
32760         return cfg;
32761     },
32762     
32763     initEvents: function() 
32764     {
32765         switch (this.size) {
32766             case 'xs' :
32767                 this.x = 1;
32768                 this.y = 1;
32769                 break;
32770             case 'sm' :
32771                 this.x = 2;
32772                 this.y = 2;
32773                 break;
32774             case 'md' :
32775             case 'md-left' :
32776             case 'md-right' :
32777                 this.x = 3;
32778                 this.y = 3;
32779                 break;
32780             case 'tall' :
32781                 this.x = 2;
32782                 this.y = 3;
32783                 break;
32784             case 'wide' :
32785                 this.x = 3;
32786                 this.y = 2;
32787                 break;
32788             case 'wide-thin' :
32789                 this.x = 3;
32790                 this.y = 1;
32791                 break;
32792                         
32793             default :
32794                 break;
32795         }
32796         
32797         if(Roo.isTouch){
32798             this.el.on('touchstart', this.onTouchStart, this);
32799             this.el.on('touchmove', this.onTouchMove, this);
32800             this.el.on('touchend', this.onTouchEnd, this);
32801             this.el.on('contextmenu', this.onContextMenu, this);
32802         } else {
32803             this.el.on('mouseenter'  ,this.enter, this);
32804             this.el.on('mouseleave', this.leave, this);
32805             this.el.on('click', this.onClick, this);
32806         }
32807         
32808         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32809             this.parent().bricks.push(this);   
32810         }
32811         
32812     },
32813     
32814     onClick: function(e, el)
32815     {
32816         var time = this.endTimer - this.startTimer;
32817         // Roo.log(e.preventDefault());
32818         if(Roo.isTouch){
32819             if(time > 1000){
32820                 e.preventDefault();
32821                 return;
32822             }
32823         }
32824         
32825         if(!this.preventDefault){
32826             return;
32827         }
32828         
32829         e.preventDefault();
32830         
32831         if (this.activeClass != '') {
32832             this.selectBrick();
32833         }
32834         
32835         this.fireEvent('click', this, e);
32836     },
32837     
32838     enter: function(e, el)
32839     {
32840         e.preventDefault();
32841         
32842         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32843             return;
32844         }
32845         
32846         if(this.bgimage.length && this.html.length){
32847             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32848         }
32849     },
32850     
32851     leave: function(e, el)
32852     {
32853         e.preventDefault();
32854         
32855         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32856             return;
32857         }
32858         
32859         if(this.bgimage.length && this.html.length){
32860             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32861         }
32862     },
32863     
32864     onTouchStart: function(e, el)
32865     {
32866 //        e.preventDefault();
32867         
32868         this.touchmoved = false;
32869         
32870         if(!this.isFitContainer){
32871             return;
32872         }
32873         
32874         if(!this.bgimage.length || !this.html.length){
32875             return;
32876         }
32877         
32878         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32879         
32880         this.timer = new Date().getTime();
32881         
32882     },
32883     
32884     onTouchMove: function(e, el)
32885     {
32886         this.touchmoved = true;
32887     },
32888     
32889     onContextMenu : function(e,el)
32890     {
32891         e.preventDefault();
32892         e.stopPropagation();
32893         return false;
32894     },
32895     
32896     onTouchEnd: function(e, el)
32897     {
32898 //        e.preventDefault();
32899         
32900         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32901         
32902             this.leave(e,el);
32903             
32904             return;
32905         }
32906         
32907         if(!this.bgimage.length || !this.html.length){
32908             
32909             if(this.href.length){
32910                 window.location.href = this.href;
32911             }
32912             
32913             return;
32914         }
32915         
32916         if(!this.isFitContainer){
32917             return;
32918         }
32919         
32920         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32921         
32922         window.location.href = this.href;
32923     },
32924     
32925     //selection on single brick only
32926     selectBrick : function() {
32927         
32928         if (!this.parentId) {
32929             return;
32930         }
32931         
32932         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32933         var index = m.selectedBrick.indexOf(this.id);
32934         
32935         if ( index > -1) {
32936             m.selectedBrick.splice(index,1);
32937             this.el.removeClass(this.activeClass);
32938             return;
32939         }
32940         
32941         for(var i = 0; i < m.selectedBrick.length; i++) {
32942             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32943             b.el.removeClass(b.activeClass);
32944         }
32945         
32946         m.selectedBrick = [];
32947         
32948         m.selectedBrick.push(this.id);
32949         this.el.addClass(this.activeClass);
32950         return;
32951     },
32952     
32953     isSelected : function(){
32954         return this.el.hasClass(this.activeClass);
32955         
32956     }
32957 });
32958
32959 Roo.apply(Roo.bootstrap.MasonryBrick, {
32960     
32961     //groups: {},
32962     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32963      /**
32964     * register a Masonry Brick
32965     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32966     */
32967     
32968     register : function(brick)
32969     {
32970         //this.groups[brick.id] = brick;
32971         this.groups.add(brick.id, brick);
32972     },
32973     /**
32974     * fetch a  masonry brick based on the masonry brick ID
32975     * @param {string} the masonry brick to add
32976     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32977     */
32978     
32979     get: function(brick_id) 
32980     {
32981         // if (typeof(this.groups[brick_id]) == 'undefined') {
32982         //     return false;
32983         // }
32984         // return this.groups[brick_id] ;
32985         
32986         if(this.groups.key(brick_id)) {
32987             return this.groups.key(brick_id);
32988         }
32989         
32990         return false;
32991     }
32992     
32993     
32994     
32995 });
32996
32997  /*
32998  * - LGPL
32999  *
33000  * element
33001  * 
33002  */
33003
33004 /**
33005  * @class Roo.bootstrap.Brick
33006  * @extends Roo.bootstrap.Component
33007  * Bootstrap Brick class
33008  * 
33009  * @constructor
33010  * Create a new Brick
33011  * @param {Object} config The config object
33012  */
33013
33014 Roo.bootstrap.Brick = function(config){
33015     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33016     
33017     this.addEvents({
33018         // raw events
33019         /**
33020          * @event click
33021          * When a Brick is click
33022          * @param {Roo.bootstrap.Brick} this
33023          * @param {Roo.EventObject} e
33024          */
33025         "click" : true
33026     });
33027 };
33028
33029 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33030     
33031     /**
33032      * @cfg {String} title
33033      */   
33034     title : '',
33035     /**
33036      * @cfg {String} html
33037      */   
33038     html : '',
33039     /**
33040      * @cfg {String} bgimage
33041      */   
33042     bgimage : '',
33043     /**
33044      * @cfg {String} cls
33045      */   
33046     cls : '',
33047     /**
33048      * @cfg {String} href
33049      */   
33050     href : '',
33051     /**
33052      * @cfg {String} video
33053      */   
33054     video : '',
33055     /**
33056      * @cfg {Boolean} square
33057      */   
33058     square : true,
33059     
33060     getAutoCreate : function()
33061     {
33062         var cls = 'roo-brick';
33063         
33064         if(this.href.length){
33065             cls += ' roo-brick-link';
33066         }
33067         
33068         if(this.bgimage.length){
33069             cls += ' roo-brick-image';
33070         }
33071         
33072         if(!this.html.length && !this.bgimage.length){
33073             cls += ' roo-brick-center-title';
33074         }
33075         
33076         if(!this.html.length && this.bgimage.length){
33077             cls += ' roo-brick-bottom-title';
33078         }
33079         
33080         if(this.cls){
33081             cls += ' ' + this.cls;
33082         }
33083         
33084         var cfg = {
33085             tag: (this.href.length) ? 'a' : 'div',
33086             cls: cls,
33087             cn: [
33088                 {
33089                     tag: 'div',
33090                     cls: 'roo-brick-paragraph',
33091                     cn: []
33092                 }
33093             ]
33094         };
33095         
33096         if(this.href.length){
33097             cfg.href = this.href;
33098         }
33099         
33100         var cn = cfg.cn[0].cn;
33101         
33102         if(this.title.length){
33103             cn.push({
33104                 tag: 'h4',
33105                 cls: 'roo-brick-title',
33106                 html: this.title
33107             });
33108         }
33109         
33110         if(this.html.length){
33111             cn.push({
33112                 tag: 'p',
33113                 cls: 'roo-brick-text',
33114                 html: this.html
33115             });
33116         } else {
33117             cn.cls += ' hide';
33118         }
33119         
33120         if(this.bgimage.length){
33121             cfg.cn.push({
33122                 tag: 'img',
33123                 cls: 'roo-brick-image-view',
33124                 src: this.bgimage
33125             });
33126         }
33127         
33128         return cfg;
33129     },
33130     
33131     initEvents: function() 
33132     {
33133         if(this.title.length || this.html.length){
33134             this.el.on('mouseenter'  ,this.enter, this);
33135             this.el.on('mouseleave', this.leave, this);
33136         }
33137         
33138         Roo.EventManager.onWindowResize(this.resize, this); 
33139         
33140         if(this.bgimage.length){
33141             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33142             this.imageEl.on('load', this.onImageLoad, this);
33143             return;
33144         }
33145         
33146         this.resize();
33147     },
33148     
33149     onImageLoad : function()
33150     {
33151         this.resize();
33152     },
33153     
33154     resize : function()
33155     {
33156         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33157         
33158         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33159         
33160         if(this.bgimage.length){
33161             var image = this.el.select('.roo-brick-image-view', true).first();
33162             
33163             image.setWidth(paragraph.getWidth());
33164             
33165             if(this.square){
33166                 image.setHeight(paragraph.getWidth());
33167             }
33168             
33169             this.el.setHeight(image.getHeight());
33170             paragraph.setHeight(image.getHeight());
33171             
33172         }
33173         
33174     },
33175     
33176     enter: function(e, el)
33177     {
33178         e.preventDefault();
33179         
33180         if(this.bgimage.length){
33181             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33182             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33183         }
33184     },
33185     
33186     leave: function(e, el)
33187     {
33188         e.preventDefault();
33189         
33190         if(this.bgimage.length){
33191             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33192             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33193         }
33194     }
33195     
33196 });
33197
33198  
33199
33200  /*
33201  * - LGPL
33202  *
33203  * Number field 
33204  */
33205
33206 /**
33207  * @class Roo.bootstrap.NumberField
33208  * @extends Roo.bootstrap.Input
33209  * Bootstrap NumberField class
33210  * 
33211  * 
33212  * 
33213  * 
33214  * @constructor
33215  * Create a new NumberField
33216  * @param {Object} config The config object
33217  */
33218
33219 Roo.bootstrap.NumberField = function(config){
33220     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33221 };
33222
33223 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33224     
33225     /**
33226      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33227      */
33228     allowDecimals : true,
33229     /**
33230      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33231      */
33232     decimalSeparator : ".",
33233     /**
33234      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33235      */
33236     decimalPrecision : 2,
33237     /**
33238      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33239      */
33240     allowNegative : true,
33241     
33242     /**
33243      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33244      */
33245     allowZero: true,
33246     /**
33247      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33248      */
33249     minValue : Number.NEGATIVE_INFINITY,
33250     /**
33251      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33252      */
33253     maxValue : Number.MAX_VALUE,
33254     /**
33255      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33256      */
33257     minText : "The minimum value for this field is {0}",
33258     /**
33259      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33260      */
33261     maxText : "The maximum value for this field is {0}",
33262     /**
33263      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33264      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33265      */
33266     nanText : "{0} is not a valid number",
33267     /**
33268      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33269      */
33270     thousandsDelimiter : false,
33271     /**
33272      * @cfg {String} valueAlign alignment of value
33273      */
33274     valueAlign : "left",
33275
33276     getAutoCreate : function()
33277     {
33278         var hiddenInput = {
33279             tag: 'input',
33280             type: 'hidden',
33281             id: Roo.id(),
33282             cls: 'hidden-number-input'
33283         };
33284         
33285         if (this.name) {
33286             hiddenInput.name = this.name;
33287         }
33288         
33289         this.name = '';
33290         
33291         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33292         
33293         this.name = hiddenInput.name;
33294         
33295         if(cfg.cn.length > 0) {
33296             cfg.cn.push(hiddenInput);
33297         }
33298         
33299         return cfg;
33300     },
33301
33302     // private
33303     initEvents : function()
33304     {   
33305         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33306         
33307         var allowed = "0123456789";
33308         
33309         if(this.allowDecimals){
33310             allowed += this.decimalSeparator;
33311         }
33312         
33313         if(this.allowNegative){
33314             allowed += "-";
33315         }
33316         
33317         if(this.thousandsDelimiter) {
33318             allowed += ",";
33319         }
33320         
33321         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33322         
33323         var keyPress = function(e){
33324             
33325             var k = e.getKey();
33326             
33327             var c = e.getCharCode();
33328             
33329             if(
33330                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33331                     allowed.indexOf(String.fromCharCode(c)) === -1
33332             ){
33333                 e.stopEvent();
33334                 return;
33335             }
33336             
33337             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33338                 return;
33339             }
33340             
33341             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33342                 e.stopEvent();
33343             }
33344         };
33345         
33346         this.el.on("keypress", keyPress, this);
33347     },
33348     
33349     validateValue : function(value)
33350     {
33351         
33352         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33353             return false;
33354         }
33355         
33356         var num = this.parseValue(value);
33357         
33358         if(isNaN(num)){
33359             this.markInvalid(String.format(this.nanText, value));
33360             return false;
33361         }
33362         
33363         if(num < this.minValue){
33364             this.markInvalid(String.format(this.minText, this.minValue));
33365             return false;
33366         }
33367         
33368         if(num > this.maxValue){
33369             this.markInvalid(String.format(this.maxText, this.maxValue));
33370             return false;
33371         }
33372         
33373         return true;
33374     },
33375
33376     getValue : function()
33377     {
33378         var v = this.hiddenEl().getValue();
33379         
33380         return this.fixPrecision(this.parseValue(v));
33381     },
33382
33383     parseValue : function(value)
33384     {
33385         if(this.thousandsDelimiter) {
33386             value += "";
33387             r = new RegExp(",", "g");
33388             value = value.replace(r, "");
33389         }
33390         
33391         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33392         return isNaN(value) ? '' : value;
33393     },
33394
33395     fixPrecision : function(value)
33396     {
33397         if(this.thousandsDelimiter) {
33398             value += "";
33399             r = new RegExp(",", "g");
33400             value = value.replace(r, "");
33401         }
33402         
33403         var nan = isNaN(value);
33404         
33405         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33406             return nan ? '' : value;
33407         }
33408         return parseFloat(value).toFixed(this.decimalPrecision);
33409     },
33410
33411     setValue : function(v)
33412     {
33413         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33414         
33415         this.value = v;
33416         
33417         if(this.rendered){
33418             
33419             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33420             
33421             this.inputEl().dom.value = (v == '') ? '' :
33422                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33423             
33424             if(!this.allowZero && v === '0') {
33425                 this.hiddenEl().dom.value = '';
33426                 this.inputEl().dom.value = '';
33427             }
33428             
33429             this.validate();
33430         }
33431     },
33432
33433     decimalPrecisionFcn : function(v)
33434     {
33435         return Math.floor(v);
33436     },
33437
33438     beforeBlur : function()
33439     {
33440         var v = this.parseValue(this.getRawValue());
33441         
33442         if(v || v === 0 || v === ''){
33443             this.setValue(v);
33444         }
33445     },
33446     
33447     hiddenEl : function()
33448     {
33449         return this.el.select('input.hidden-number-input',true).first();
33450     }
33451     
33452 });
33453
33454  
33455
33456 /*
33457 * Licence: LGPL
33458 */
33459
33460 /**
33461  * @class Roo.bootstrap.DocumentSlider
33462  * @extends Roo.bootstrap.Component
33463  * Bootstrap DocumentSlider class
33464  * 
33465  * @constructor
33466  * Create a new DocumentViewer
33467  * @param {Object} config The config object
33468  */
33469
33470 Roo.bootstrap.DocumentSlider = function(config){
33471     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33472     
33473     this.files = [];
33474     
33475     this.addEvents({
33476         /**
33477          * @event initial
33478          * Fire after initEvent
33479          * @param {Roo.bootstrap.DocumentSlider} this
33480          */
33481         "initial" : true,
33482         /**
33483          * @event update
33484          * Fire after update
33485          * @param {Roo.bootstrap.DocumentSlider} this
33486          */
33487         "update" : true,
33488         /**
33489          * @event click
33490          * Fire after click
33491          * @param {Roo.bootstrap.DocumentSlider} this
33492          */
33493         "click" : true
33494     });
33495 };
33496
33497 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33498     
33499     files : false,
33500     
33501     indicator : 0,
33502     
33503     getAutoCreate : function()
33504     {
33505         var cfg = {
33506             tag : 'div',
33507             cls : 'roo-document-slider',
33508             cn : [
33509                 {
33510                     tag : 'div',
33511                     cls : 'roo-document-slider-header',
33512                     cn : [
33513                         {
33514                             tag : 'div',
33515                             cls : 'roo-document-slider-header-title'
33516                         }
33517                     ]
33518                 },
33519                 {
33520                     tag : 'div',
33521                     cls : 'roo-document-slider-body',
33522                     cn : [
33523                         {
33524                             tag : 'div',
33525                             cls : 'roo-document-slider-prev',
33526                             cn : [
33527                                 {
33528                                     tag : 'i',
33529                                     cls : 'fa fa-chevron-left'
33530                                 }
33531                             ]
33532                         },
33533                         {
33534                             tag : 'div',
33535                             cls : 'roo-document-slider-thumb',
33536                             cn : [
33537                                 {
33538                                     tag : 'img',
33539                                     cls : 'roo-document-slider-image'
33540                                 }
33541                             ]
33542                         },
33543                         {
33544                             tag : 'div',
33545                             cls : 'roo-document-slider-next',
33546                             cn : [
33547                                 {
33548                                     tag : 'i',
33549                                     cls : 'fa fa-chevron-right'
33550                                 }
33551                             ]
33552                         }
33553                     ]
33554                 }
33555             ]
33556         };
33557         
33558         return cfg;
33559     },
33560     
33561     initEvents : function()
33562     {
33563         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33564         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33565         
33566         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33567         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33568         
33569         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33570         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33571         
33572         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33573         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33574         
33575         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33576         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33577         
33578         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33579         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33580         
33581         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33582         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33583         
33584         this.thumbEl.on('click', this.onClick, this);
33585         
33586         this.prevIndicator.on('click', this.prev, this);
33587         
33588         this.nextIndicator.on('click', this.next, this);
33589         
33590     },
33591     
33592     initial : function()
33593     {
33594         if(this.files.length){
33595             this.indicator = 1;
33596             this.update()
33597         }
33598         
33599         this.fireEvent('initial', this);
33600     },
33601     
33602     update : function()
33603     {
33604         this.imageEl.attr('src', this.files[this.indicator - 1]);
33605         
33606         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33607         
33608         this.prevIndicator.show();
33609         
33610         if(this.indicator == 1){
33611             this.prevIndicator.hide();
33612         }
33613         
33614         this.nextIndicator.show();
33615         
33616         if(this.indicator == this.files.length){
33617             this.nextIndicator.hide();
33618         }
33619         
33620         this.thumbEl.scrollTo('top');
33621         
33622         this.fireEvent('update', this);
33623     },
33624     
33625     onClick : function(e)
33626     {
33627         e.preventDefault();
33628         
33629         this.fireEvent('click', this);
33630     },
33631     
33632     prev : function(e)
33633     {
33634         e.preventDefault();
33635         
33636         this.indicator = Math.max(1, this.indicator - 1);
33637         
33638         this.update();
33639     },
33640     
33641     next : function(e)
33642     {
33643         e.preventDefault();
33644         
33645         this.indicator = Math.min(this.files.length, this.indicator + 1);
33646         
33647         this.update();
33648     }
33649 });
33650 /*
33651  * - LGPL
33652  *
33653  * RadioSet
33654  *
33655  *
33656  */
33657
33658 /**
33659  * @class Roo.bootstrap.RadioSet
33660  * @extends Roo.bootstrap.Input
33661  * Bootstrap RadioSet class
33662  * @cfg {String} indicatorpos (left|right) default left
33663  * @cfg {Boolean} inline (true|false) inline the element (default true)
33664  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33665  * @constructor
33666  * Create a new RadioSet
33667  * @param {Object} config The config object
33668  */
33669
33670 Roo.bootstrap.RadioSet = function(config){
33671     
33672     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33673     
33674     this.radioes = [];
33675     
33676     Roo.bootstrap.RadioSet.register(this);
33677     
33678     this.addEvents({
33679         /**
33680         * @event check
33681         * Fires when the element is checked or unchecked.
33682         * @param {Roo.bootstrap.RadioSet} this This radio
33683         * @param {Roo.bootstrap.Radio} item The checked item
33684         */
33685        check : true,
33686        /**
33687         * @event click
33688         * Fires when the element is click.
33689         * @param {Roo.bootstrap.RadioSet} this This radio set
33690         * @param {Roo.bootstrap.Radio} item The checked item
33691         * @param {Roo.EventObject} e The event object
33692         */
33693        click : true
33694     });
33695     
33696 };
33697
33698 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33699
33700     radioes : false,
33701     
33702     inline : true,
33703     
33704     weight : '',
33705     
33706     indicatorpos : 'left',
33707     
33708     getAutoCreate : function()
33709     {
33710         var label = {
33711             tag : 'label',
33712             cls : 'roo-radio-set-label',
33713             cn : [
33714                 {
33715                     tag : 'span',
33716                     html : this.fieldLabel
33717                 }
33718             ]
33719         };
33720         
33721         if(this.indicatorpos == 'left'){
33722             label.cn.unshift({
33723                 tag : 'i',
33724                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33725                 tooltip : 'This field is required'
33726             });
33727         } else {
33728             label.cn.push({
33729                 tag : 'i',
33730                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33731                 tooltip : 'This field is required'
33732             });
33733         }
33734         
33735         var items = {
33736             tag : 'div',
33737             cls : 'roo-radio-set-items'
33738         };
33739         
33740         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33741         
33742         if (align === 'left' && this.fieldLabel.length) {
33743             
33744             items = {
33745                 cls : "roo-radio-set-right", 
33746                 cn: [
33747                     items
33748                 ]
33749             };
33750             
33751             if(this.labelWidth > 12){
33752                 label.style = "width: " + this.labelWidth + 'px';
33753             }
33754             
33755             if(this.labelWidth < 13 && this.labelmd == 0){
33756                 this.labelmd = this.labelWidth;
33757             }
33758             
33759             if(this.labellg > 0){
33760                 label.cls += ' col-lg-' + this.labellg;
33761                 items.cls += ' col-lg-' + (12 - this.labellg);
33762             }
33763             
33764             if(this.labelmd > 0){
33765                 label.cls += ' col-md-' + this.labelmd;
33766                 items.cls += ' col-md-' + (12 - this.labelmd);
33767             }
33768             
33769             if(this.labelsm > 0){
33770                 label.cls += ' col-sm-' + this.labelsm;
33771                 items.cls += ' col-sm-' + (12 - this.labelsm);
33772             }
33773             
33774             if(this.labelxs > 0){
33775                 label.cls += ' col-xs-' + this.labelxs;
33776                 items.cls += ' col-xs-' + (12 - this.labelxs);
33777             }
33778         }
33779         
33780         var cfg = {
33781             tag : 'div',
33782             cls : 'roo-radio-set',
33783             cn : [
33784                 {
33785                     tag : 'input',
33786                     cls : 'roo-radio-set-input',
33787                     type : 'hidden',
33788                     name : this.name,
33789                     value : this.value ? this.value :  ''
33790                 },
33791                 label,
33792                 items
33793             ]
33794         };
33795         
33796         if(this.weight.length){
33797             cfg.cls += ' roo-radio-' + this.weight;
33798         }
33799         
33800         if(this.inline) {
33801             cfg.cls += ' roo-radio-set-inline';
33802         }
33803         
33804         var settings=this;
33805         ['xs','sm','md','lg'].map(function(size){
33806             if (settings[size]) {
33807                 cfg.cls += ' col-' + size + '-' + settings[size];
33808             }
33809         });
33810         
33811         return cfg;
33812         
33813     },
33814
33815     initEvents : function()
33816     {
33817         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33818         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33819         
33820         if(!this.fieldLabel.length){
33821             this.labelEl.hide();
33822         }
33823         
33824         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33825         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33826         
33827         this.indicator = this.indicatorEl();
33828         
33829         if(this.indicator){
33830             this.indicator.addClass('invisible');
33831         }
33832         
33833         this.originalValue = this.getValue();
33834         
33835     },
33836     
33837     inputEl: function ()
33838     {
33839         return this.el.select('.roo-radio-set-input', true).first();
33840     },
33841     
33842     getChildContainer : function()
33843     {
33844         return this.itemsEl;
33845     },
33846     
33847     register : function(item)
33848     {
33849         this.radioes.push(item);
33850         
33851     },
33852     
33853     validate : function()
33854     {   
33855         if(this.getVisibilityEl().hasClass('hidden')){
33856             return true;
33857         }
33858         
33859         var valid = false;
33860         
33861         Roo.each(this.radioes, function(i){
33862             if(!i.checked){
33863                 return;
33864             }
33865             
33866             valid = true;
33867             return false;
33868         });
33869         
33870         if(this.allowBlank) {
33871             return true;
33872         }
33873         
33874         if(this.disabled || valid){
33875             this.markValid();
33876             return true;
33877         }
33878         
33879         this.markInvalid();
33880         return false;
33881         
33882     },
33883     
33884     markValid : function()
33885     {
33886         if(this.labelEl.isVisible(true)){
33887             this.indicatorEl().removeClass('visible');
33888             this.indicatorEl().addClass('invisible');
33889         }
33890         
33891         this.el.removeClass([this.invalidClass, this.validClass]);
33892         this.el.addClass(this.validClass);
33893         
33894         this.fireEvent('valid', this);
33895     },
33896     
33897     markInvalid : function(msg)
33898     {
33899         if(this.allowBlank || this.disabled){
33900             return;
33901         }
33902         
33903         if(this.labelEl.isVisible(true)){
33904             this.indicatorEl().removeClass('invisible');
33905             this.indicatorEl().addClass('visible');
33906         }
33907         
33908         this.el.removeClass([this.invalidClass, this.validClass]);
33909         this.el.addClass(this.invalidClass);
33910         
33911         this.fireEvent('invalid', this, msg);
33912         
33913     },
33914     
33915     setValue : function(v, suppressEvent)
33916     {   
33917         if(this.value === v){
33918             return;
33919         }
33920         
33921         this.value = v;
33922         
33923         if(this.rendered){
33924             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33925         }
33926         
33927         Roo.each(this.radioes, function(i){
33928             i.checked = false;
33929             i.el.removeClass('checked');
33930         });
33931         
33932         Roo.each(this.radioes, function(i){
33933             
33934             if(i.value === v || i.value.toString() === v.toString()){
33935                 i.checked = true;
33936                 i.el.addClass('checked');
33937                 
33938                 if(suppressEvent !== true){
33939                     this.fireEvent('check', this, i);
33940                 }
33941                 
33942                 return false;
33943             }
33944             
33945         }, this);
33946         
33947         this.validate();
33948     },
33949     
33950     clearInvalid : function(){
33951         
33952         if(!this.el || this.preventMark){
33953             return;
33954         }
33955         
33956         this.el.removeClass([this.invalidClass]);
33957         
33958         this.fireEvent('valid', this);
33959     }
33960     
33961 });
33962
33963 Roo.apply(Roo.bootstrap.RadioSet, {
33964     
33965     groups: {},
33966     
33967     register : function(set)
33968     {
33969         this.groups[set.name] = set;
33970     },
33971     
33972     get: function(name) 
33973     {
33974         if (typeof(this.groups[name]) == 'undefined') {
33975             return false;
33976         }
33977         
33978         return this.groups[name] ;
33979     }
33980     
33981 });
33982 /*
33983  * Based on:
33984  * Ext JS Library 1.1.1
33985  * Copyright(c) 2006-2007, Ext JS, LLC.
33986  *
33987  * Originally Released Under LGPL - original licence link has changed is not relivant.
33988  *
33989  * Fork - LGPL
33990  * <script type="text/javascript">
33991  */
33992
33993
33994 /**
33995  * @class Roo.bootstrap.SplitBar
33996  * @extends Roo.util.Observable
33997  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33998  * <br><br>
33999  * Usage:
34000  * <pre><code>
34001 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34002                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34003 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34004 split.minSize = 100;
34005 split.maxSize = 600;
34006 split.animate = true;
34007 split.on('moved', splitterMoved);
34008 </code></pre>
34009  * @constructor
34010  * Create a new SplitBar
34011  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34012  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34013  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34014  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34015                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34016                         position of the SplitBar).
34017  */
34018 Roo.bootstrap.SplitBar = function(cfg){
34019     
34020     /** @private */
34021     
34022     //{
34023     //  dragElement : elm
34024     //  resizingElement: el,
34025         // optional..
34026     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34027     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34028         // existingProxy ???
34029     //}
34030     
34031     this.el = Roo.get(cfg.dragElement, true);
34032     this.el.dom.unselectable = "on";
34033     /** @private */
34034     this.resizingEl = Roo.get(cfg.resizingElement, true);
34035
34036     /**
34037      * @private
34038      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34039      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34040      * @type Number
34041      */
34042     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34043     
34044     /**
34045      * The minimum size of the resizing element. (Defaults to 0)
34046      * @type Number
34047      */
34048     this.minSize = 0;
34049     
34050     /**
34051      * The maximum size of the resizing element. (Defaults to 2000)
34052      * @type Number
34053      */
34054     this.maxSize = 2000;
34055     
34056     /**
34057      * Whether to animate the transition to the new size
34058      * @type Boolean
34059      */
34060     this.animate = false;
34061     
34062     /**
34063      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34064      * @type Boolean
34065      */
34066     this.useShim = false;
34067     
34068     /** @private */
34069     this.shim = null;
34070     
34071     if(!cfg.existingProxy){
34072         /** @private */
34073         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34074     }else{
34075         this.proxy = Roo.get(cfg.existingProxy).dom;
34076     }
34077     /** @private */
34078     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34079     
34080     /** @private */
34081     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34082     
34083     /** @private */
34084     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34085     
34086     /** @private */
34087     this.dragSpecs = {};
34088     
34089     /**
34090      * @private The adapter to use to positon and resize elements
34091      */
34092     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34093     this.adapter.init(this);
34094     
34095     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34096         /** @private */
34097         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34098         this.el.addClass("roo-splitbar-h");
34099     }else{
34100         /** @private */
34101         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34102         this.el.addClass("roo-splitbar-v");
34103     }
34104     
34105     this.addEvents({
34106         /**
34107          * @event resize
34108          * Fires when the splitter is moved (alias for {@link #event-moved})
34109          * @param {Roo.bootstrap.SplitBar} this
34110          * @param {Number} newSize the new width or height
34111          */
34112         "resize" : true,
34113         /**
34114          * @event moved
34115          * Fires when the splitter is moved
34116          * @param {Roo.bootstrap.SplitBar} this
34117          * @param {Number} newSize the new width or height
34118          */
34119         "moved" : true,
34120         /**
34121          * @event beforeresize
34122          * Fires before the splitter is dragged
34123          * @param {Roo.bootstrap.SplitBar} this
34124          */
34125         "beforeresize" : true,
34126
34127         "beforeapply" : true
34128     });
34129
34130     Roo.util.Observable.call(this);
34131 };
34132
34133 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34134     onStartProxyDrag : function(x, y){
34135         this.fireEvent("beforeresize", this);
34136         if(!this.overlay){
34137             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34138             o.unselectable();
34139             o.enableDisplayMode("block");
34140             // all splitbars share the same overlay
34141             Roo.bootstrap.SplitBar.prototype.overlay = o;
34142         }
34143         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34144         this.overlay.show();
34145         Roo.get(this.proxy).setDisplayed("block");
34146         var size = this.adapter.getElementSize(this);
34147         this.activeMinSize = this.getMinimumSize();;
34148         this.activeMaxSize = this.getMaximumSize();;
34149         var c1 = size - this.activeMinSize;
34150         var c2 = Math.max(this.activeMaxSize - size, 0);
34151         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34152             this.dd.resetConstraints();
34153             this.dd.setXConstraint(
34154                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34155                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34156             );
34157             this.dd.setYConstraint(0, 0);
34158         }else{
34159             this.dd.resetConstraints();
34160             this.dd.setXConstraint(0, 0);
34161             this.dd.setYConstraint(
34162                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34163                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34164             );
34165          }
34166         this.dragSpecs.startSize = size;
34167         this.dragSpecs.startPoint = [x, y];
34168         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34169     },
34170     
34171     /** 
34172      * @private Called after the drag operation by the DDProxy
34173      */
34174     onEndProxyDrag : function(e){
34175         Roo.get(this.proxy).setDisplayed(false);
34176         var endPoint = Roo.lib.Event.getXY(e);
34177         if(this.overlay){
34178             this.overlay.hide();
34179         }
34180         var newSize;
34181         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34182             newSize = this.dragSpecs.startSize + 
34183                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34184                     endPoint[0] - this.dragSpecs.startPoint[0] :
34185                     this.dragSpecs.startPoint[0] - endPoint[0]
34186                 );
34187         }else{
34188             newSize = this.dragSpecs.startSize + 
34189                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34190                     endPoint[1] - this.dragSpecs.startPoint[1] :
34191                     this.dragSpecs.startPoint[1] - endPoint[1]
34192                 );
34193         }
34194         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34195         if(newSize != this.dragSpecs.startSize){
34196             if(this.fireEvent('beforeapply', this, newSize) !== false){
34197                 this.adapter.setElementSize(this, newSize);
34198                 this.fireEvent("moved", this, newSize);
34199                 this.fireEvent("resize", this, newSize);
34200             }
34201         }
34202     },
34203     
34204     /**
34205      * Get the adapter this SplitBar uses
34206      * @return The adapter object
34207      */
34208     getAdapter : function(){
34209         return this.adapter;
34210     },
34211     
34212     /**
34213      * Set the adapter this SplitBar uses
34214      * @param {Object} adapter A SplitBar adapter object
34215      */
34216     setAdapter : function(adapter){
34217         this.adapter = adapter;
34218         this.adapter.init(this);
34219     },
34220     
34221     /**
34222      * Gets the minimum size for the resizing element
34223      * @return {Number} The minimum size
34224      */
34225     getMinimumSize : function(){
34226         return this.minSize;
34227     },
34228     
34229     /**
34230      * Sets the minimum size for the resizing element
34231      * @param {Number} minSize The minimum size
34232      */
34233     setMinimumSize : function(minSize){
34234         this.minSize = minSize;
34235     },
34236     
34237     /**
34238      * Gets the maximum size for the resizing element
34239      * @return {Number} The maximum size
34240      */
34241     getMaximumSize : function(){
34242         return this.maxSize;
34243     },
34244     
34245     /**
34246      * Sets the maximum size for the resizing element
34247      * @param {Number} maxSize The maximum size
34248      */
34249     setMaximumSize : function(maxSize){
34250         this.maxSize = maxSize;
34251     },
34252     
34253     /**
34254      * Sets the initialize size for the resizing element
34255      * @param {Number} size The initial size
34256      */
34257     setCurrentSize : function(size){
34258         var oldAnimate = this.animate;
34259         this.animate = false;
34260         this.adapter.setElementSize(this, size);
34261         this.animate = oldAnimate;
34262     },
34263     
34264     /**
34265      * Destroy this splitbar. 
34266      * @param {Boolean} removeEl True to remove the element
34267      */
34268     destroy : function(removeEl){
34269         if(this.shim){
34270             this.shim.remove();
34271         }
34272         this.dd.unreg();
34273         this.proxy.parentNode.removeChild(this.proxy);
34274         if(removeEl){
34275             this.el.remove();
34276         }
34277     }
34278 });
34279
34280 /**
34281  * @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.
34282  */
34283 Roo.bootstrap.SplitBar.createProxy = function(dir){
34284     var proxy = new Roo.Element(document.createElement("div"));
34285     proxy.unselectable();
34286     var cls = 'roo-splitbar-proxy';
34287     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34288     document.body.appendChild(proxy.dom);
34289     return proxy.dom;
34290 };
34291
34292 /** 
34293  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34294  * Default Adapter. It assumes the splitter and resizing element are not positioned
34295  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34296  */
34297 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34298 };
34299
34300 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34301     // do nothing for now
34302     init : function(s){
34303     
34304     },
34305     /**
34306      * Called before drag operations to get the current size of the resizing element. 
34307      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34308      */
34309      getElementSize : function(s){
34310         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34311             return s.resizingEl.getWidth();
34312         }else{
34313             return s.resizingEl.getHeight();
34314         }
34315     },
34316     
34317     /**
34318      * Called after drag operations to set the size of the resizing element.
34319      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34320      * @param {Number} newSize The new size to set
34321      * @param {Function} onComplete A function to be invoked when resizing is complete
34322      */
34323     setElementSize : function(s, newSize, onComplete){
34324         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34325             if(!s.animate){
34326                 s.resizingEl.setWidth(newSize);
34327                 if(onComplete){
34328                     onComplete(s, newSize);
34329                 }
34330             }else{
34331                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34332             }
34333         }else{
34334             
34335             if(!s.animate){
34336                 s.resizingEl.setHeight(newSize);
34337                 if(onComplete){
34338                     onComplete(s, newSize);
34339                 }
34340             }else{
34341                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34342             }
34343         }
34344     }
34345 };
34346
34347 /** 
34348  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34349  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34350  * Adapter that  moves the splitter element to align with the resized sizing element. 
34351  * Used with an absolute positioned SplitBar.
34352  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34353  * document.body, make sure you assign an id to the body element.
34354  */
34355 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34356     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34357     this.container = Roo.get(container);
34358 };
34359
34360 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34361     init : function(s){
34362         this.basic.init(s);
34363     },
34364     
34365     getElementSize : function(s){
34366         return this.basic.getElementSize(s);
34367     },
34368     
34369     setElementSize : function(s, newSize, onComplete){
34370         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34371     },
34372     
34373     moveSplitter : function(s){
34374         var yes = Roo.bootstrap.SplitBar;
34375         switch(s.placement){
34376             case yes.LEFT:
34377                 s.el.setX(s.resizingEl.getRight());
34378                 break;
34379             case yes.RIGHT:
34380                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34381                 break;
34382             case yes.TOP:
34383                 s.el.setY(s.resizingEl.getBottom());
34384                 break;
34385             case yes.BOTTOM:
34386                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34387                 break;
34388         }
34389     }
34390 };
34391
34392 /**
34393  * Orientation constant - Create a vertical SplitBar
34394  * @static
34395  * @type Number
34396  */
34397 Roo.bootstrap.SplitBar.VERTICAL = 1;
34398
34399 /**
34400  * Orientation constant - Create a horizontal SplitBar
34401  * @static
34402  * @type Number
34403  */
34404 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34405
34406 /**
34407  * Placement constant - The resizing element is to the left of the splitter element
34408  * @static
34409  * @type Number
34410  */
34411 Roo.bootstrap.SplitBar.LEFT = 1;
34412
34413 /**
34414  * Placement constant - The resizing element is to the right of the splitter element
34415  * @static
34416  * @type Number
34417  */
34418 Roo.bootstrap.SplitBar.RIGHT = 2;
34419
34420 /**
34421  * Placement constant - The resizing element is positioned above the splitter element
34422  * @static
34423  * @type Number
34424  */
34425 Roo.bootstrap.SplitBar.TOP = 3;
34426
34427 /**
34428  * Placement constant - The resizing element is positioned under splitter element
34429  * @static
34430  * @type Number
34431  */
34432 Roo.bootstrap.SplitBar.BOTTOM = 4;
34433 Roo.namespace("Roo.bootstrap.layout");/*
34434  * Based on:
34435  * Ext JS Library 1.1.1
34436  * Copyright(c) 2006-2007, Ext JS, LLC.
34437  *
34438  * Originally Released Under LGPL - original licence link has changed is not relivant.
34439  *
34440  * Fork - LGPL
34441  * <script type="text/javascript">
34442  */
34443
34444 /**
34445  * @class Roo.bootstrap.layout.Manager
34446  * @extends Roo.bootstrap.Component
34447  * Base class for layout managers.
34448  */
34449 Roo.bootstrap.layout.Manager = function(config)
34450 {
34451     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34452
34453
34454
34455
34456
34457     /** false to disable window resize monitoring @type Boolean */
34458     this.monitorWindowResize = true;
34459     this.regions = {};
34460     this.addEvents({
34461         /**
34462          * @event layout
34463          * Fires when a layout is performed.
34464          * @param {Roo.LayoutManager} this
34465          */
34466         "layout" : true,
34467         /**
34468          * @event regionresized
34469          * Fires when the user resizes a region.
34470          * @param {Roo.LayoutRegion} region The resized region
34471          * @param {Number} newSize The new size (width for east/west, height for north/south)
34472          */
34473         "regionresized" : true,
34474         /**
34475          * @event regioncollapsed
34476          * Fires when a region is collapsed.
34477          * @param {Roo.LayoutRegion} region The collapsed region
34478          */
34479         "regioncollapsed" : true,
34480         /**
34481          * @event regionexpanded
34482          * Fires when a region is expanded.
34483          * @param {Roo.LayoutRegion} region The expanded region
34484          */
34485         "regionexpanded" : true
34486     });
34487     this.updating = false;
34488
34489     if (config.el) {
34490         this.el = Roo.get(config.el);
34491         this.initEvents();
34492     }
34493
34494 };
34495
34496 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34497
34498
34499     regions : null,
34500
34501     monitorWindowResize : true,
34502
34503
34504     updating : false,
34505
34506
34507     onRender : function(ct, position)
34508     {
34509         if(!this.el){
34510             this.el = Roo.get(ct);
34511             this.initEvents();
34512         }
34513         //this.fireEvent('render',this);
34514     },
34515
34516
34517     initEvents: function()
34518     {
34519
34520
34521         // ie scrollbar fix
34522         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34523             document.body.scroll = "no";
34524         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34525             this.el.position('relative');
34526         }
34527         this.id = this.el.id;
34528         this.el.addClass("roo-layout-container");
34529         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34530         if(this.el.dom != document.body ) {
34531             this.el.on('resize', this.layout,this);
34532             this.el.on('show', this.layout,this);
34533         }
34534
34535     },
34536
34537     /**
34538      * Returns true if this layout is currently being updated
34539      * @return {Boolean}
34540      */
34541     isUpdating : function(){
34542         return this.updating;
34543     },
34544
34545     /**
34546      * Suspend the LayoutManager from doing auto-layouts while
34547      * making multiple add or remove calls
34548      */
34549     beginUpdate : function(){
34550         this.updating = true;
34551     },
34552
34553     /**
34554      * Restore auto-layouts and optionally disable the manager from performing a layout
34555      * @param {Boolean} noLayout true to disable a layout update
34556      */
34557     endUpdate : function(noLayout){
34558         this.updating = false;
34559         if(!noLayout){
34560             this.layout();
34561         }
34562     },
34563
34564     layout: function(){
34565         // abstract...
34566     },
34567
34568     onRegionResized : function(region, newSize){
34569         this.fireEvent("regionresized", region, newSize);
34570         this.layout();
34571     },
34572
34573     onRegionCollapsed : function(region){
34574         this.fireEvent("regioncollapsed", region);
34575     },
34576
34577     onRegionExpanded : function(region){
34578         this.fireEvent("regionexpanded", region);
34579     },
34580
34581     /**
34582      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34583      * performs box-model adjustments.
34584      * @return {Object} The size as an object {width: (the width), height: (the height)}
34585      */
34586     getViewSize : function()
34587     {
34588         var size;
34589         if(this.el.dom != document.body){
34590             size = this.el.getSize();
34591         }else{
34592             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34593         }
34594         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34595         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34596         return size;
34597     },
34598
34599     /**
34600      * Returns the Element this layout is bound to.
34601      * @return {Roo.Element}
34602      */
34603     getEl : function(){
34604         return this.el;
34605     },
34606
34607     /**
34608      * Returns the specified region.
34609      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34610      * @return {Roo.LayoutRegion}
34611      */
34612     getRegion : function(target){
34613         return this.regions[target.toLowerCase()];
34614     },
34615
34616     onWindowResize : function(){
34617         if(this.monitorWindowResize){
34618             this.layout();
34619         }
34620     }
34621 });
34622 /*
34623  * Based on:
34624  * Ext JS Library 1.1.1
34625  * Copyright(c) 2006-2007, Ext JS, LLC.
34626  *
34627  * Originally Released Under LGPL - original licence link has changed is not relivant.
34628  *
34629  * Fork - LGPL
34630  * <script type="text/javascript">
34631  */
34632 /**
34633  * @class Roo.bootstrap.layout.Border
34634  * @extends Roo.bootstrap.layout.Manager
34635  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34636  * please see: examples/bootstrap/nested.html<br><br>
34637  
34638 <b>The container the layout is rendered into can be either the body element or any other element.
34639 If it is not the body element, the container needs to either be an absolute positioned element,
34640 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34641 the container size if it is not the body element.</b>
34642
34643 * @constructor
34644 * Create a new Border
34645 * @param {Object} config Configuration options
34646  */
34647 Roo.bootstrap.layout.Border = function(config){
34648     config = config || {};
34649     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34650     
34651     
34652     
34653     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34654         if(config[region]){
34655             config[region].region = region;
34656             this.addRegion(config[region]);
34657         }
34658     },this);
34659     
34660 };
34661
34662 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34663
34664 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34665     /**
34666      * Creates and adds a new region if it doesn't already exist.
34667      * @param {String} target The target region key (north, south, east, west or center).
34668      * @param {Object} config The regions config object
34669      * @return {BorderLayoutRegion} The new region
34670      */
34671     addRegion : function(config)
34672     {
34673         if(!this.regions[config.region]){
34674             var r = this.factory(config);
34675             this.bindRegion(r);
34676         }
34677         return this.regions[config.region];
34678     },
34679
34680     // private (kinda)
34681     bindRegion : function(r){
34682         this.regions[r.config.region] = r;
34683         
34684         r.on("visibilitychange",    this.layout, this);
34685         r.on("paneladded",          this.layout, this);
34686         r.on("panelremoved",        this.layout, this);
34687         r.on("invalidated",         this.layout, this);
34688         r.on("resized",             this.onRegionResized, this);
34689         r.on("collapsed",           this.onRegionCollapsed, this);
34690         r.on("expanded",            this.onRegionExpanded, this);
34691     },
34692
34693     /**
34694      * Performs a layout update.
34695      */
34696     layout : function()
34697     {
34698         if(this.updating) {
34699             return;
34700         }
34701         
34702         // render all the rebions if they have not been done alreayd?
34703         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34704             if(this.regions[region] && !this.regions[region].bodyEl){
34705                 this.regions[region].onRender(this.el)
34706             }
34707         },this);
34708         
34709         var size = this.getViewSize();
34710         var w = size.width;
34711         var h = size.height;
34712         var centerW = w;
34713         var centerH = h;
34714         var centerY = 0;
34715         var centerX = 0;
34716         //var x = 0, y = 0;
34717
34718         var rs = this.regions;
34719         var north = rs["north"];
34720         var south = rs["south"]; 
34721         var west = rs["west"];
34722         var east = rs["east"];
34723         var center = rs["center"];
34724         //if(this.hideOnLayout){ // not supported anymore
34725             //c.el.setStyle("display", "none");
34726         //}
34727         if(north && north.isVisible()){
34728             var b = north.getBox();
34729             var m = north.getMargins();
34730             b.width = w - (m.left+m.right);
34731             b.x = m.left;
34732             b.y = m.top;
34733             centerY = b.height + b.y + m.bottom;
34734             centerH -= centerY;
34735             north.updateBox(this.safeBox(b));
34736         }
34737         if(south && south.isVisible()){
34738             var b = south.getBox();
34739             var m = south.getMargins();
34740             b.width = w - (m.left+m.right);
34741             b.x = m.left;
34742             var totalHeight = (b.height + m.top + m.bottom);
34743             b.y = h - totalHeight + m.top;
34744             centerH -= totalHeight;
34745             south.updateBox(this.safeBox(b));
34746         }
34747         if(west && west.isVisible()){
34748             var b = west.getBox();
34749             var m = west.getMargins();
34750             b.height = centerH - (m.top+m.bottom);
34751             b.x = m.left;
34752             b.y = centerY + m.top;
34753             var totalWidth = (b.width + m.left + m.right);
34754             centerX += totalWidth;
34755             centerW -= totalWidth;
34756             west.updateBox(this.safeBox(b));
34757         }
34758         if(east && east.isVisible()){
34759             var b = east.getBox();
34760             var m = east.getMargins();
34761             b.height = centerH - (m.top+m.bottom);
34762             var totalWidth = (b.width + m.left + m.right);
34763             b.x = w - totalWidth + m.left;
34764             b.y = centerY + m.top;
34765             centerW -= totalWidth;
34766             east.updateBox(this.safeBox(b));
34767         }
34768         if(center){
34769             var m = center.getMargins();
34770             var centerBox = {
34771                 x: centerX + m.left,
34772                 y: centerY + m.top,
34773                 width: centerW - (m.left+m.right),
34774                 height: centerH - (m.top+m.bottom)
34775             };
34776             //if(this.hideOnLayout){
34777                 //center.el.setStyle("display", "block");
34778             //}
34779             center.updateBox(this.safeBox(centerBox));
34780         }
34781         this.el.repaint();
34782         this.fireEvent("layout", this);
34783     },
34784
34785     // private
34786     safeBox : function(box){
34787         box.width = Math.max(0, box.width);
34788         box.height = Math.max(0, box.height);
34789         return box;
34790     },
34791
34792     /**
34793      * Adds a ContentPanel (or subclass) to this layout.
34794      * @param {String} target The target region key (north, south, east, west or center).
34795      * @param {Roo.ContentPanel} panel The panel to add
34796      * @return {Roo.ContentPanel} The added panel
34797      */
34798     add : function(target, panel){
34799          
34800         target = target.toLowerCase();
34801         return this.regions[target].add(panel);
34802     },
34803
34804     /**
34805      * Remove a ContentPanel (or subclass) to this layout.
34806      * @param {String} target The target region key (north, south, east, west or center).
34807      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34808      * @return {Roo.ContentPanel} The removed panel
34809      */
34810     remove : function(target, panel){
34811         target = target.toLowerCase();
34812         return this.regions[target].remove(panel);
34813     },
34814
34815     /**
34816      * Searches all regions for a panel with the specified id
34817      * @param {String} panelId
34818      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34819      */
34820     findPanel : function(panelId){
34821         var rs = this.regions;
34822         for(var target in rs){
34823             if(typeof rs[target] != "function"){
34824                 var p = rs[target].getPanel(panelId);
34825                 if(p){
34826                     return p;
34827                 }
34828             }
34829         }
34830         return null;
34831     },
34832
34833     /**
34834      * Searches all regions for a panel with the specified id and activates (shows) it.
34835      * @param {String/ContentPanel} panelId The panels id or the panel itself
34836      * @return {Roo.ContentPanel} The shown panel or null
34837      */
34838     showPanel : function(panelId) {
34839       var rs = this.regions;
34840       for(var target in rs){
34841          var r = rs[target];
34842          if(typeof r != "function"){
34843             if(r.hasPanel(panelId)){
34844                return r.showPanel(panelId);
34845             }
34846          }
34847       }
34848       return null;
34849    },
34850
34851    /**
34852      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34853      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34854      */
34855    /*
34856     restoreState : function(provider){
34857         if(!provider){
34858             provider = Roo.state.Manager;
34859         }
34860         var sm = new Roo.LayoutStateManager();
34861         sm.init(this, provider);
34862     },
34863 */
34864  
34865  
34866     /**
34867      * Adds a xtype elements to the layout.
34868      * <pre><code>
34869
34870 layout.addxtype({
34871        xtype : 'ContentPanel',
34872        region: 'west',
34873        items: [ .... ]
34874    }
34875 );
34876
34877 layout.addxtype({
34878         xtype : 'NestedLayoutPanel',
34879         region: 'west',
34880         layout: {
34881            center: { },
34882            west: { }   
34883         },
34884         items : [ ... list of content panels or nested layout panels.. ]
34885    }
34886 );
34887 </code></pre>
34888      * @param {Object} cfg Xtype definition of item to add.
34889      */
34890     addxtype : function(cfg)
34891     {
34892         // basically accepts a pannel...
34893         // can accept a layout region..!?!?
34894         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34895         
34896         
34897         // theory?  children can only be panels??
34898         
34899         //if (!cfg.xtype.match(/Panel$/)) {
34900         //    return false;
34901         //}
34902         var ret = false;
34903         
34904         if (typeof(cfg.region) == 'undefined') {
34905             Roo.log("Failed to add Panel, region was not set");
34906             Roo.log(cfg);
34907             return false;
34908         }
34909         var region = cfg.region;
34910         delete cfg.region;
34911         
34912           
34913         var xitems = [];
34914         if (cfg.items) {
34915             xitems = cfg.items;
34916             delete cfg.items;
34917         }
34918         var nb = false;
34919         
34920         switch(cfg.xtype) 
34921         {
34922             case 'Content':  // ContentPanel (el, cfg)
34923             case 'Scroll':  // ContentPanel (el, cfg)
34924             case 'View': 
34925                 cfg.autoCreate = true;
34926                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34927                 //} else {
34928                 //    var el = this.el.createChild();
34929                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34930                 //}
34931                 
34932                 this.add(region, ret);
34933                 break;
34934             
34935             /*
34936             case 'TreePanel': // our new panel!
34937                 cfg.el = this.el.createChild();
34938                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34939                 this.add(region, ret);
34940                 break;
34941             */
34942             
34943             case 'Nest': 
34944                 // create a new Layout (which is  a Border Layout...
34945                 
34946                 var clayout = cfg.layout;
34947                 clayout.el  = this.el.createChild();
34948                 clayout.items   = clayout.items  || [];
34949                 
34950                 delete cfg.layout;
34951                 
34952                 // replace this exitems with the clayout ones..
34953                 xitems = clayout.items;
34954                  
34955                 // force background off if it's in center...
34956                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34957                     cfg.background = false;
34958                 }
34959                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34960                 
34961                 
34962                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34963                 //console.log('adding nested layout panel '  + cfg.toSource());
34964                 this.add(region, ret);
34965                 nb = {}; /// find first...
34966                 break;
34967             
34968             case 'Grid':
34969                 
34970                 // needs grid and region
34971                 
34972                 //var el = this.getRegion(region).el.createChild();
34973                 /*
34974                  *var el = this.el.createChild();
34975                 // create the grid first...
34976                 cfg.grid.container = el;
34977                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34978                 */
34979                 
34980                 if (region == 'center' && this.active ) {
34981                     cfg.background = false;
34982                 }
34983                 
34984                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34985                 
34986                 this.add(region, ret);
34987                 /*
34988                 if (cfg.background) {
34989                     // render grid on panel activation (if panel background)
34990                     ret.on('activate', function(gp) {
34991                         if (!gp.grid.rendered) {
34992                     //        gp.grid.render(el);
34993                         }
34994                     });
34995                 } else {
34996                   //  cfg.grid.render(el);
34997                 }
34998                 */
34999                 break;
35000            
35001            
35002             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35003                 // it was the old xcomponent building that caused this before.
35004                 // espeically if border is the top element in the tree.
35005                 ret = this;
35006                 break; 
35007                 
35008                     
35009                 
35010                 
35011                 
35012             default:
35013                 /*
35014                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35015                     
35016                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35017                     this.add(region, ret);
35018                 } else {
35019                 */
35020                     Roo.log(cfg);
35021                     throw "Can not add '" + cfg.xtype + "' to Border";
35022                     return null;
35023              
35024                                 
35025              
35026         }
35027         this.beginUpdate();
35028         // add children..
35029         var region = '';
35030         var abn = {};
35031         Roo.each(xitems, function(i)  {
35032             region = nb && i.region ? i.region : false;
35033             
35034             var add = ret.addxtype(i);
35035            
35036             if (region) {
35037                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35038                 if (!i.background) {
35039                     abn[region] = nb[region] ;
35040                 }
35041             }
35042             
35043         });
35044         this.endUpdate();
35045
35046         // make the last non-background panel active..
35047         //if (nb) { Roo.log(abn); }
35048         if (nb) {
35049             
35050             for(var r in abn) {
35051                 region = this.getRegion(r);
35052                 if (region) {
35053                     // tried using nb[r], but it does not work..
35054                      
35055                     region.showPanel(abn[r]);
35056                    
35057                 }
35058             }
35059         }
35060         return ret;
35061         
35062     },
35063     
35064     
35065 // private
35066     factory : function(cfg)
35067     {
35068         
35069         var validRegions = Roo.bootstrap.layout.Border.regions;
35070
35071         var target = cfg.region;
35072         cfg.mgr = this;
35073         
35074         var r = Roo.bootstrap.layout;
35075         Roo.log(target);
35076         switch(target){
35077             case "north":
35078                 return new r.North(cfg);
35079             case "south":
35080                 return new r.South(cfg);
35081             case "east":
35082                 return new r.East(cfg);
35083             case "west":
35084                 return new r.West(cfg);
35085             case "center":
35086                 return new r.Center(cfg);
35087         }
35088         throw 'Layout region "'+target+'" not supported.';
35089     }
35090     
35091     
35092 });
35093  /*
35094  * Based on:
35095  * Ext JS Library 1.1.1
35096  * Copyright(c) 2006-2007, Ext JS, LLC.
35097  *
35098  * Originally Released Under LGPL - original licence link has changed is not relivant.
35099  *
35100  * Fork - LGPL
35101  * <script type="text/javascript">
35102  */
35103  
35104 /**
35105  * @class Roo.bootstrap.layout.Basic
35106  * @extends Roo.util.Observable
35107  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35108  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35109  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35110  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35111  * @cfg {string}   region  the region that it inhabits..
35112  * @cfg {bool}   skipConfig skip config?
35113  * 
35114
35115  */
35116 Roo.bootstrap.layout.Basic = function(config){
35117     
35118     this.mgr = config.mgr;
35119     
35120     this.position = config.region;
35121     
35122     var skipConfig = config.skipConfig;
35123     
35124     this.events = {
35125         /**
35126          * @scope Roo.BasicLayoutRegion
35127          */
35128         
35129         /**
35130          * @event beforeremove
35131          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35132          * @param {Roo.LayoutRegion} this
35133          * @param {Roo.ContentPanel} panel The panel
35134          * @param {Object} e The cancel event object
35135          */
35136         "beforeremove" : true,
35137         /**
35138          * @event invalidated
35139          * Fires when the layout for this region is changed.
35140          * @param {Roo.LayoutRegion} this
35141          */
35142         "invalidated" : true,
35143         /**
35144          * @event visibilitychange
35145          * Fires when this region is shown or hidden 
35146          * @param {Roo.LayoutRegion} this
35147          * @param {Boolean} visibility true or false
35148          */
35149         "visibilitychange" : true,
35150         /**
35151          * @event paneladded
35152          * Fires when a panel is added. 
35153          * @param {Roo.LayoutRegion} this
35154          * @param {Roo.ContentPanel} panel The panel
35155          */
35156         "paneladded" : true,
35157         /**
35158          * @event panelremoved
35159          * Fires when a panel is removed. 
35160          * @param {Roo.LayoutRegion} this
35161          * @param {Roo.ContentPanel} panel The panel
35162          */
35163         "panelremoved" : true,
35164         /**
35165          * @event beforecollapse
35166          * Fires when this region before collapse.
35167          * @param {Roo.LayoutRegion} this
35168          */
35169         "beforecollapse" : true,
35170         /**
35171          * @event collapsed
35172          * Fires when this region is collapsed.
35173          * @param {Roo.LayoutRegion} this
35174          */
35175         "collapsed" : true,
35176         /**
35177          * @event expanded
35178          * Fires when this region is expanded.
35179          * @param {Roo.LayoutRegion} this
35180          */
35181         "expanded" : true,
35182         /**
35183          * @event slideshow
35184          * Fires when this region is slid into view.
35185          * @param {Roo.LayoutRegion} this
35186          */
35187         "slideshow" : true,
35188         /**
35189          * @event slidehide
35190          * Fires when this region slides out of view. 
35191          * @param {Roo.LayoutRegion} this
35192          */
35193         "slidehide" : true,
35194         /**
35195          * @event panelactivated
35196          * Fires when a panel is activated. 
35197          * @param {Roo.LayoutRegion} this
35198          * @param {Roo.ContentPanel} panel The activated panel
35199          */
35200         "panelactivated" : true,
35201         /**
35202          * @event resized
35203          * Fires when the user resizes this region. 
35204          * @param {Roo.LayoutRegion} this
35205          * @param {Number} newSize The new size (width for east/west, height for north/south)
35206          */
35207         "resized" : true
35208     };
35209     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35210     this.panels = new Roo.util.MixedCollection();
35211     this.panels.getKey = this.getPanelId.createDelegate(this);
35212     this.box = null;
35213     this.activePanel = null;
35214     // ensure listeners are added...
35215     
35216     if (config.listeners || config.events) {
35217         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35218             listeners : config.listeners || {},
35219             events : config.events || {}
35220         });
35221     }
35222     
35223     if(skipConfig !== true){
35224         this.applyConfig(config);
35225     }
35226 };
35227
35228 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35229 {
35230     getPanelId : function(p){
35231         return p.getId();
35232     },
35233     
35234     applyConfig : function(config){
35235         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35236         this.config = config;
35237         
35238     },
35239     
35240     /**
35241      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35242      * the width, for horizontal (north, south) the height.
35243      * @param {Number} newSize The new width or height
35244      */
35245     resizeTo : function(newSize){
35246         var el = this.el ? this.el :
35247                  (this.activePanel ? this.activePanel.getEl() : null);
35248         if(el){
35249             switch(this.position){
35250                 case "east":
35251                 case "west":
35252                     el.setWidth(newSize);
35253                     this.fireEvent("resized", this, newSize);
35254                 break;
35255                 case "north":
35256                 case "south":
35257                     el.setHeight(newSize);
35258                     this.fireEvent("resized", this, newSize);
35259                 break;                
35260             }
35261         }
35262     },
35263     
35264     getBox : function(){
35265         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35266     },
35267     
35268     getMargins : function(){
35269         return this.margins;
35270     },
35271     
35272     updateBox : function(box){
35273         this.box = box;
35274         var el = this.activePanel.getEl();
35275         el.dom.style.left = box.x + "px";
35276         el.dom.style.top = box.y + "px";
35277         this.activePanel.setSize(box.width, box.height);
35278     },
35279     
35280     /**
35281      * Returns the container element for this region.
35282      * @return {Roo.Element}
35283      */
35284     getEl : function(){
35285         return this.activePanel;
35286     },
35287     
35288     /**
35289      * Returns true if this region is currently visible.
35290      * @return {Boolean}
35291      */
35292     isVisible : function(){
35293         return this.activePanel ? true : false;
35294     },
35295     
35296     setActivePanel : function(panel){
35297         panel = this.getPanel(panel);
35298         if(this.activePanel && this.activePanel != panel){
35299             this.activePanel.setActiveState(false);
35300             this.activePanel.getEl().setLeftTop(-10000,-10000);
35301         }
35302         this.activePanel = panel;
35303         panel.setActiveState(true);
35304         if(this.box){
35305             panel.setSize(this.box.width, this.box.height);
35306         }
35307         this.fireEvent("panelactivated", this, panel);
35308         this.fireEvent("invalidated");
35309     },
35310     
35311     /**
35312      * Show the specified panel.
35313      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35314      * @return {Roo.ContentPanel} The shown panel or null
35315      */
35316     showPanel : function(panel){
35317         panel = this.getPanel(panel);
35318         if(panel){
35319             this.setActivePanel(panel);
35320         }
35321         return panel;
35322     },
35323     
35324     /**
35325      * Get the active panel for this region.
35326      * @return {Roo.ContentPanel} The active panel or null
35327      */
35328     getActivePanel : function(){
35329         return this.activePanel;
35330     },
35331     
35332     /**
35333      * Add the passed ContentPanel(s)
35334      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35335      * @return {Roo.ContentPanel} The panel added (if only one was added)
35336      */
35337     add : function(panel){
35338         if(arguments.length > 1){
35339             for(var i = 0, len = arguments.length; i < len; i++) {
35340                 this.add(arguments[i]);
35341             }
35342             return null;
35343         }
35344         if(this.hasPanel(panel)){
35345             this.showPanel(panel);
35346             return panel;
35347         }
35348         var el = panel.getEl();
35349         if(el.dom.parentNode != this.mgr.el.dom){
35350             this.mgr.el.dom.appendChild(el.dom);
35351         }
35352         if(panel.setRegion){
35353             panel.setRegion(this);
35354         }
35355         this.panels.add(panel);
35356         el.setStyle("position", "absolute");
35357         if(!panel.background){
35358             this.setActivePanel(panel);
35359             if(this.config.initialSize && this.panels.getCount()==1){
35360                 this.resizeTo(this.config.initialSize);
35361             }
35362         }
35363         this.fireEvent("paneladded", this, panel);
35364         return panel;
35365     },
35366     
35367     /**
35368      * Returns true if the panel is in this region.
35369      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35370      * @return {Boolean}
35371      */
35372     hasPanel : function(panel){
35373         if(typeof panel == "object"){ // must be panel obj
35374             panel = panel.getId();
35375         }
35376         return this.getPanel(panel) ? true : false;
35377     },
35378     
35379     /**
35380      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35381      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35382      * @param {Boolean} preservePanel Overrides the config preservePanel option
35383      * @return {Roo.ContentPanel} The panel that was removed
35384      */
35385     remove : function(panel, preservePanel){
35386         panel = this.getPanel(panel);
35387         if(!panel){
35388             return null;
35389         }
35390         var e = {};
35391         this.fireEvent("beforeremove", this, panel, e);
35392         if(e.cancel === true){
35393             return null;
35394         }
35395         var panelId = panel.getId();
35396         this.panels.removeKey(panelId);
35397         return panel;
35398     },
35399     
35400     /**
35401      * Returns the panel specified or null if it's not in this region.
35402      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35403      * @return {Roo.ContentPanel}
35404      */
35405     getPanel : function(id){
35406         if(typeof id == "object"){ // must be panel obj
35407             return id;
35408         }
35409         return this.panels.get(id);
35410     },
35411     
35412     /**
35413      * Returns this regions position (north/south/east/west/center).
35414      * @return {String} 
35415      */
35416     getPosition: function(){
35417         return this.position;    
35418     }
35419 });/*
35420  * Based on:
35421  * Ext JS Library 1.1.1
35422  * Copyright(c) 2006-2007, Ext JS, LLC.
35423  *
35424  * Originally Released Under LGPL - original licence link has changed is not relivant.
35425  *
35426  * Fork - LGPL
35427  * <script type="text/javascript">
35428  */
35429  
35430 /**
35431  * @class Roo.bootstrap.layout.Region
35432  * @extends Roo.bootstrap.layout.Basic
35433  * This class represents a region in a layout manager.
35434  
35435  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35436  * @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})
35437  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35438  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35439  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35440  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35441  * @cfg {String}    title           The title for the region (overrides panel titles)
35442  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35443  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35444  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35445  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35446  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35447  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35448  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35449  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35450  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35451  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35452
35453  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35454  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35455  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35456  * @cfg {Number}    width           For East/West panels
35457  * @cfg {Number}    height          For North/South panels
35458  * @cfg {Boolean}   split           To show the splitter
35459  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35460  * 
35461  * @cfg {string}   cls             Extra CSS classes to add to region
35462  * 
35463  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35464  * @cfg {string}   region  the region that it inhabits..
35465  *
35466
35467  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35468  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35469
35470  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35471  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35472  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35473  */
35474 Roo.bootstrap.layout.Region = function(config)
35475 {
35476     this.applyConfig(config);
35477
35478     var mgr = config.mgr;
35479     var pos = config.region;
35480     config.skipConfig = true;
35481     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35482     
35483     if (mgr.el) {
35484         this.onRender(mgr.el);   
35485     }
35486      
35487     this.visible = true;
35488     this.collapsed = false;
35489     this.unrendered_panels = [];
35490 };
35491
35492 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35493
35494     position: '', // set by wrapper (eg. north/south etc..)
35495     unrendered_panels : null,  // unrendered panels.
35496     createBody : function(){
35497         /** This region's body element 
35498         * @type Roo.Element */
35499         this.bodyEl = this.el.createChild({
35500                 tag: "div",
35501                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35502         });
35503     },
35504
35505     onRender: function(ctr, pos)
35506     {
35507         var dh = Roo.DomHelper;
35508         /** This region's container element 
35509         * @type Roo.Element */
35510         this.el = dh.append(ctr.dom, {
35511                 tag: "div",
35512                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35513             }, true);
35514         /** This region's title element 
35515         * @type Roo.Element */
35516     
35517         this.titleEl = dh.append(this.el.dom,
35518             {
35519                     tag: "div",
35520                     unselectable: "on",
35521                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35522                     children:[
35523                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35524                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35525                     ]}, true);
35526         
35527         this.titleEl.enableDisplayMode();
35528         /** This region's title text element 
35529         * @type HTMLElement */
35530         this.titleTextEl = this.titleEl.dom.firstChild;
35531         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35532         /*
35533         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35534         this.closeBtn.enableDisplayMode();
35535         this.closeBtn.on("click", this.closeClicked, this);
35536         this.closeBtn.hide();
35537     */
35538         this.createBody(this.config);
35539         if(this.config.hideWhenEmpty){
35540             this.hide();
35541             this.on("paneladded", this.validateVisibility, this);
35542             this.on("panelremoved", this.validateVisibility, this);
35543         }
35544         if(this.autoScroll){
35545             this.bodyEl.setStyle("overflow", "auto");
35546         }else{
35547             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35548         }
35549         //if(c.titlebar !== false){
35550             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35551                 this.titleEl.hide();
35552             }else{
35553                 this.titleEl.show();
35554                 if(this.config.title){
35555                     this.titleTextEl.innerHTML = this.config.title;
35556                 }
35557             }
35558         //}
35559         if(this.config.collapsed){
35560             this.collapse(true);
35561         }
35562         if(this.config.hidden){
35563             this.hide();
35564         }
35565         
35566         if (this.unrendered_panels && this.unrendered_panels.length) {
35567             for (var i =0;i< this.unrendered_panels.length; i++) {
35568                 this.add(this.unrendered_panels[i]);
35569             }
35570             this.unrendered_panels = null;
35571             
35572         }
35573         
35574     },
35575     
35576     applyConfig : function(c)
35577     {
35578         /*
35579          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35580             var dh = Roo.DomHelper;
35581             if(c.titlebar !== false){
35582                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35583                 this.collapseBtn.on("click", this.collapse, this);
35584                 this.collapseBtn.enableDisplayMode();
35585                 /*
35586                 if(c.showPin === true || this.showPin){
35587                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35588                     this.stickBtn.enableDisplayMode();
35589                     this.stickBtn.on("click", this.expand, this);
35590                     this.stickBtn.hide();
35591                 }
35592                 
35593             }
35594             */
35595             /** This region's collapsed element
35596             * @type Roo.Element */
35597             /*
35598              *
35599             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35600                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35601             ]}, true);
35602             
35603             if(c.floatable !== false){
35604                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35605                this.collapsedEl.on("click", this.collapseClick, this);
35606             }
35607
35608             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35609                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35610                    id: "message", unselectable: "on", style:{"float":"left"}});
35611                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35612              }
35613             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35614             this.expandBtn.on("click", this.expand, this);
35615             
35616         }
35617         
35618         if(this.collapseBtn){
35619             this.collapseBtn.setVisible(c.collapsible == true);
35620         }
35621         
35622         this.cmargins = c.cmargins || this.cmargins ||
35623                          (this.position == "west" || this.position == "east" ?
35624                              {top: 0, left: 2, right:2, bottom: 0} :
35625                              {top: 2, left: 0, right:0, bottom: 2});
35626         */
35627         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35628         
35629         
35630         this.bottomTabs = c.tabPosition != "top";
35631         
35632         this.autoScroll = c.autoScroll || false;
35633         
35634         
35635        
35636         
35637         this.duration = c.duration || .30;
35638         this.slideDuration = c.slideDuration || .45;
35639         this.config = c;
35640        
35641     },
35642     /**
35643      * Returns true if this region is currently visible.
35644      * @return {Boolean}
35645      */
35646     isVisible : function(){
35647         return this.visible;
35648     },
35649
35650     /**
35651      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35652      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35653      */
35654     //setCollapsedTitle : function(title){
35655     //    title = title || "&#160;";
35656      //   if(this.collapsedTitleTextEl){
35657       //      this.collapsedTitleTextEl.innerHTML = title;
35658        // }
35659     //},
35660
35661     getBox : function(){
35662         var b;
35663       //  if(!this.collapsed){
35664             b = this.el.getBox(false, true);
35665        // }else{
35666           //  b = this.collapsedEl.getBox(false, true);
35667         //}
35668         return b;
35669     },
35670
35671     getMargins : function(){
35672         return this.margins;
35673         //return this.collapsed ? this.cmargins : this.margins;
35674     },
35675 /*
35676     highlight : function(){
35677         this.el.addClass("x-layout-panel-dragover");
35678     },
35679
35680     unhighlight : function(){
35681         this.el.removeClass("x-layout-panel-dragover");
35682     },
35683 */
35684     updateBox : function(box)
35685     {
35686         if (!this.bodyEl) {
35687             return; // not rendered yet..
35688         }
35689         
35690         this.box = box;
35691         if(!this.collapsed){
35692             this.el.dom.style.left = box.x + "px";
35693             this.el.dom.style.top = box.y + "px";
35694             this.updateBody(box.width, box.height);
35695         }else{
35696             this.collapsedEl.dom.style.left = box.x + "px";
35697             this.collapsedEl.dom.style.top = box.y + "px";
35698             this.collapsedEl.setSize(box.width, box.height);
35699         }
35700         if(this.tabs){
35701             this.tabs.autoSizeTabs();
35702         }
35703     },
35704
35705     updateBody : function(w, h)
35706     {
35707         if(w !== null){
35708             this.el.setWidth(w);
35709             w -= this.el.getBorderWidth("rl");
35710             if(this.config.adjustments){
35711                 w += this.config.adjustments[0];
35712             }
35713         }
35714         if(h !== null && h > 0){
35715             this.el.setHeight(h);
35716             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35717             h -= this.el.getBorderWidth("tb");
35718             if(this.config.adjustments){
35719                 h += this.config.adjustments[1];
35720             }
35721             this.bodyEl.setHeight(h);
35722             if(this.tabs){
35723                 h = this.tabs.syncHeight(h);
35724             }
35725         }
35726         if(this.panelSize){
35727             w = w !== null ? w : this.panelSize.width;
35728             h = h !== null ? h : this.panelSize.height;
35729         }
35730         if(this.activePanel){
35731             var el = this.activePanel.getEl();
35732             w = w !== null ? w : el.getWidth();
35733             h = h !== null ? h : el.getHeight();
35734             this.panelSize = {width: w, height: h};
35735             this.activePanel.setSize(w, h);
35736         }
35737         if(Roo.isIE && this.tabs){
35738             this.tabs.el.repaint();
35739         }
35740     },
35741
35742     /**
35743      * Returns the container element for this region.
35744      * @return {Roo.Element}
35745      */
35746     getEl : function(){
35747         return this.el;
35748     },
35749
35750     /**
35751      * Hides this region.
35752      */
35753     hide : function(){
35754         //if(!this.collapsed){
35755             this.el.dom.style.left = "-2000px";
35756             this.el.hide();
35757         //}else{
35758          //   this.collapsedEl.dom.style.left = "-2000px";
35759          //   this.collapsedEl.hide();
35760        // }
35761         this.visible = false;
35762         this.fireEvent("visibilitychange", this, false);
35763     },
35764
35765     /**
35766      * Shows this region if it was previously hidden.
35767      */
35768     show : function(){
35769         //if(!this.collapsed){
35770             this.el.show();
35771         //}else{
35772         //    this.collapsedEl.show();
35773        // }
35774         this.visible = true;
35775         this.fireEvent("visibilitychange", this, true);
35776     },
35777 /*
35778     closeClicked : function(){
35779         if(this.activePanel){
35780             this.remove(this.activePanel);
35781         }
35782     },
35783
35784     collapseClick : function(e){
35785         if(this.isSlid){
35786            e.stopPropagation();
35787            this.slideIn();
35788         }else{
35789            e.stopPropagation();
35790            this.slideOut();
35791         }
35792     },
35793 */
35794     /**
35795      * Collapses this region.
35796      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35797      */
35798     /*
35799     collapse : function(skipAnim, skipCheck = false){
35800         if(this.collapsed) {
35801             return;
35802         }
35803         
35804         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35805             
35806             this.collapsed = true;
35807             if(this.split){
35808                 this.split.el.hide();
35809             }
35810             if(this.config.animate && skipAnim !== true){
35811                 this.fireEvent("invalidated", this);
35812                 this.animateCollapse();
35813             }else{
35814                 this.el.setLocation(-20000,-20000);
35815                 this.el.hide();
35816                 this.collapsedEl.show();
35817                 this.fireEvent("collapsed", this);
35818                 this.fireEvent("invalidated", this);
35819             }
35820         }
35821         
35822     },
35823 */
35824     animateCollapse : function(){
35825         // overridden
35826     },
35827
35828     /**
35829      * Expands this region if it was previously collapsed.
35830      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35831      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35832      */
35833     /*
35834     expand : function(e, skipAnim){
35835         if(e) {
35836             e.stopPropagation();
35837         }
35838         if(!this.collapsed || this.el.hasActiveFx()) {
35839             return;
35840         }
35841         if(this.isSlid){
35842             this.afterSlideIn();
35843             skipAnim = true;
35844         }
35845         this.collapsed = false;
35846         if(this.config.animate && skipAnim !== true){
35847             this.animateExpand();
35848         }else{
35849             this.el.show();
35850             if(this.split){
35851                 this.split.el.show();
35852             }
35853             this.collapsedEl.setLocation(-2000,-2000);
35854             this.collapsedEl.hide();
35855             this.fireEvent("invalidated", this);
35856             this.fireEvent("expanded", this);
35857         }
35858     },
35859 */
35860     animateExpand : function(){
35861         // overridden
35862     },
35863
35864     initTabs : function()
35865     {
35866         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35867         
35868         var ts = new Roo.bootstrap.panel.Tabs({
35869                 el: this.bodyEl.dom,
35870                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35871                 disableTooltips: this.config.disableTabTips,
35872                 toolbar : this.config.toolbar
35873             });
35874         
35875         if(this.config.hideTabs){
35876             ts.stripWrap.setDisplayed(false);
35877         }
35878         this.tabs = ts;
35879         ts.resizeTabs = this.config.resizeTabs === true;
35880         ts.minTabWidth = this.config.minTabWidth || 40;
35881         ts.maxTabWidth = this.config.maxTabWidth || 250;
35882         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35883         ts.monitorResize = false;
35884         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35885         ts.bodyEl.addClass('roo-layout-tabs-body');
35886         this.panels.each(this.initPanelAsTab, this);
35887     },
35888
35889     initPanelAsTab : function(panel){
35890         var ti = this.tabs.addTab(
35891             panel.getEl().id,
35892             panel.getTitle(),
35893             null,
35894             this.config.closeOnTab && panel.isClosable(),
35895             panel.tpl
35896         );
35897         if(panel.tabTip !== undefined){
35898             ti.setTooltip(panel.tabTip);
35899         }
35900         ti.on("activate", function(){
35901               this.setActivePanel(panel);
35902         }, this);
35903         
35904         if(this.config.closeOnTab){
35905             ti.on("beforeclose", function(t, e){
35906                 e.cancel = true;
35907                 this.remove(panel);
35908             }, this);
35909         }
35910         
35911         panel.tabItem = ti;
35912         
35913         return ti;
35914     },
35915
35916     updatePanelTitle : function(panel, title)
35917     {
35918         if(this.activePanel == panel){
35919             this.updateTitle(title);
35920         }
35921         if(this.tabs){
35922             var ti = this.tabs.getTab(panel.getEl().id);
35923             ti.setText(title);
35924             if(panel.tabTip !== undefined){
35925                 ti.setTooltip(panel.tabTip);
35926             }
35927         }
35928     },
35929
35930     updateTitle : function(title){
35931         if(this.titleTextEl && !this.config.title){
35932             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35933         }
35934     },
35935
35936     setActivePanel : function(panel)
35937     {
35938         panel = this.getPanel(panel);
35939         if(this.activePanel && this.activePanel != panel){
35940             if(this.activePanel.setActiveState(false) === false){
35941                 return;
35942             }
35943         }
35944         this.activePanel = panel;
35945         panel.setActiveState(true);
35946         if(this.panelSize){
35947             panel.setSize(this.panelSize.width, this.panelSize.height);
35948         }
35949         if(this.closeBtn){
35950             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35951         }
35952         this.updateTitle(panel.getTitle());
35953         if(this.tabs){
35954             this.fireEvent("invalidated", this);
35955         }
35956         this.fireEvent("panelactivated", this, panel);
35957     },
35958
35959     /**
35960      * Shows the specified panel.
35961      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35962      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35963      */
35964     showPanel : function(panel)
35965     {
35966         panel = this.getPanel(panel);
35967         if(panel){
35968             if(this.tabs){
35969                 var tab = this.tabs.getTab(panel.getEl().id);
35970                 if(tab.isHidden()){
35971                     this.tabs.unhideTab(tab.id);
35972                 }
35973                 tab.activate();
35974             }else{
35975                 this.setActivePanel(panel);
35976             }
35977         }
35978         return panel;
35979     },
35980
35981     /**
35982      * Get the active panel for this region.
35983      * @return {Roo.ContentPanel} The active panel or null
35984      */
35985     getActivePanel : function(){
35986         return this.activePanel;
35987     },
35988
35989     validateVisibility : function(){
35990         if(this.panels.getCount() < 1){
35991             this.updateTitle("&#160;");
35992             this.closeBtn.hide();
35993             this.hide();
35994         }else{
35995             if(!this.isVisible()){
35996                 this.show();
35997             }
35998         }
35999     },
36000
36001     /**
36002      * Adds the passed ContentPanel(s) to this region.
36003      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36004      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36005      */
36006     add : function(panel)
36007     {
36008         if(arguments.length > 1){
36009             for(var i = 0, len = arguments.length; i < len; i++) {
36010                 this.add(arguments[i]);
36011             }
36012             return null;
36013         }
36014         
36015         // if we have not been rendered yet, then we can not really do much of this..
36016         if (!this.bodyEl) {
36017             this.unrendered_panels.push(panel);
36018             return panel;
36019         }
36020         
36021         
36022         
36023         
36024         if(this.hasPanel(panel)){
36025             this.showPanel(panel);
36026             return panel;
36027         }
36028         panel.setRegion(this);
36029         this.panels.add(panel);
36030        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36031             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36032             // and hide them... ???
36033             this.bodyEl.dom.appendChild(panel.getEl().dom);
36034             if(panel.background !== true){
36035                 this.setActivePanel(panel);
36036             }
36037             this.fireEvent("paneladded", this, panel);
36038             return panel;
36039         }
36040         */
36041         if(!this.tabs){
36042             this.initTabs();
36043         }else{
36044             this.initPanelAsTab(panel);
36045         }
36046         
36047         
36048         if(panel.background !== true){
36049             this.tabs.activate(panel.getEl().id);
36050         }
36051         this.fireEvent("paneladded", this, panel);
36052         return panel;
36053     },
36054
36055     /**
36056      * Hides the tab for the specified panel.
36057      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36058      */
36059     hidePanel : function(panel){
36060         if(this.tabs && (panel = this.getPanel(panel))){
36061             this.tabs.hideTab(panel.getEl().id);
36062         }
36063     },
36064
36065     /**
36066      * Unhides the tab for a previously hidden panel.
36067      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36068      */
36069     unhidePanel : function(panel){
36070         if(this.tabs && (panel = this.getPanel(panel))){
36071             this.tabs.unhideTab(panel.getEl().id);
36072         }
36073     },
36074
36075     clearPanels : function(){
36076         while(this.panels.getCount() > 0){
36077              this.remove(this.panels.first());
36078         }
36079     },
36080
36081     /**
36082      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36083      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36084      * @param {Boolean} preservePanel Overrides the config preservePanel option
36085      * @return {Roo.ContentPanel} The panel that was removed
36086      */
36087     remove : function(panel, preservePanel)
36088     {
36089         panel = this.getPanel(panel);
36090         if(!panel){
36091             return null;
36092         }
36093         var e = {};
36094         this.fireEvent("beforeremove", this, panel, e);
36095         if(e.cancel === true){
36096             return null;
36097         }
36098         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36099         var panelId = panel.getId();
36100         this.panels.removeKey(panelId);
36101         if(preservePanel){
36102             document.body.appendChild(panel.getEl().dom);
36103         }
36104         if(this.tabs){
36105             this.tabs.removeTab(panel.getEl().id);
36106         }else if (!preservePanel){
36107             this.bodyEl.dom.removeChild(panel.getEl().dom);
36108         }
36109         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36110             var p = this.panels.first();
36111             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36112             tempEl.appendChild(p.getEl().dom);
36113             this.bodyEl.update("");
36114             this.bodyEl.dom.appendChild(p.getEl().dom);
36115             tempEl = null;
36116             this.updateTitle(p.getTitle());
36117             this.tabs = null;
36118             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36119             this.setActivePanel(p);
36120         }
36121         panel.setRegion(null);
36122         if(this.activePanel == panel){
36123             this.activePanel = null;
36124         }
36125         if(this.config.autoDestroy !== false && preservePanel !== true){
36126             try{panel.destroy();}catch(e){}
36127         }
36128         this.fireEvent("panelremoved", this, panel);
36129         return panel;
36130     },
36131
36132     /**
36133      * Returns the TabPanel component used by this region
36134      * @return {Roo.TabPanel}
36135      */
36136     getTabs : function(){
36137         return this.tabs;
36138     },
36139
36140     createTool : function(parentEl, className){
36141         var btn = Roo.DomHelper.append(parentEl, {
36142             tag: "div",
36143             cls: "x-layout-tools-button",
36144             children: [ {
36145                 tag: "div",
36146                 cls: "roo-layout-tools-button-inner " + className,
36147                 html: "&#160;"
36148             }]
36149         }, true);
36150         btn.addClassOnOver("roo-layout-tools-button-over");
36151         return btn;
36152     }
36153 });/*
36154  * Based on:
36155  * Ext JS Library 1.1.1
36156  * Copyright(c) 2006-2007, Ext JS, LLC.
36157  *
36158  * Originally Released Under LGPL - original licence link has changed is not relivant.
36159  *
36160  * Fork - LGPL
36161  * <script type="text/javascript">
36162  */
36163  
36164
36165
36166 /**
36167  * @class Roo.SplitLayoutRegion
36168  * @extends Roo.LayoutRegion
36169  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36170  */
36171 Roo.bootstrap.layout.Split = function(config){
36172     this.cursor = config.cursor;
36173     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36174 };
36175
36176 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36177 {
36178     splitTip : "Drag to resize.",
36179     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36180     useSplitTips : false,
36181
36182     applyConfig : function(config){
36183         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36184     },
36185     
36186     onRender : function(ctr,pos) {
36187         
36188         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36189         if(!this.config.split){
36190             return;
36191         }
36192         if(!this.split){
36193             
36194             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36195                             tag: "div",
36196                             id: this.el.id + "-split",
36197                             cls: "roo-layout-split roo-layout-split-"+this.position,
36198                             html: "&#160;"
36199             });
36200             /** The SplitBar for this region 
36201             * @type Roo.SplitBar */
36202             // does not exist yet...
36203             Roo.log([this.position, this.orientation]);
36204             
36205             this.split = new Roo.bootstrap.SplitBar({
36206                 dragElement : splitEl,
36207                 resizingElement: this.el,
36208                 orientation : this.orientation
36209             });
36210             
36211             this.split.on("moved", this.onSplitMove, this);
36212             this.split.useShim = this.config.useShim === true;
36213             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36214             if(this.useSplitTips){
36215                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36216             }
36217             //if(config.collapsible){
36218             //    this.split.el.on("dblclick", this.collapse,  this);
36219             //}
36220         }
36221         if(typeof this.config.minSize != "undefined"){
36222             this.split.minSize = this.config.minSize;
36223         }
36224         if(typeof this.config.maxSize != "undefined"){
36225             this.split.maxSize = this.config.maxSize;
36226         }
36227         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36228             this.hideSplitter();
36229         }
36230         
36231     },
36232
36233     getHMaxSize : function(){
36234          var cmax = this.config.maxSize || 10000;
36235          var center = this.mgr.getRegion("center");
36236          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36237     },
36238
36239     getVMaxSize : function(){
36240          var cmax = this.config.maxSize || 10000;
36241          var center = this.mgr.getRegion("center");
36242          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36243     },
36244
36245     onSplitMove : function(split, newSize){
36246         this.fireEvent("resized", this, newSize);
36247     },
36248     
36249     /** 
36250      * Returns the {@link Roo.SplitBar} for this region.
36251      * @return {Roo.SplitBar}
36252      */
36253     getSplitBar : function(){
36254         return this.split;
36255     },
36256     
36257     hide : function(){
36258         this.hideSplitter();
36259         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36260     },
36261
36262     hideSplitter : function(){
36263         if(this.split){
36264             this.split.el.setLocation(-2000,-2000);
36265             this.split.el.hide();
36266         }
36267     },
36268
36269     show : function(){
36270         if(this.split){
36271             this.split.el.show();
36272         }
36273         Roo.bootstrap.layout.Split.superclass.show.call(this);
36274     },
36275     
36276     beforeSlide: function(){
36277         if(Roo.isGecko){// firefox overflow auto bug workaround
36278             this.bodyEl.clip();
36279             if(this.tabs) {
36280                 this.tabs.bodyEl.clip();
36281             }
36282             if(this.activePanel){
36283                 this.activePanel.getEl().clip();
36284                 
36285                 if(this.activePanel.beforeSlide){
36286                     this.activePanel.beforeSlide();
36287                 }
36288             }
36289         }
36290     },
36291     
36292     afterSlide : function(){
36293         if(Roo.isGecko){// firefox overflow auto bug workaround
36294             this.bodyEl.unclip();
36295             if(this.tabs) {
36296                 this.tabs.bodyEl.unclip();
36297             }
36298             if(this.activePanel){
36299                 this.activePanel.getEl().unclip();
36300                 if(this.activePanel.afterSlide){
36301                     this.activePanel.afterSlide();
36302                 }
36303             }
36304         }
36305     },
36306
36307     initAutoHide : function(){
36308         if(this.autoHide !== false){
36309             if(!this.autoHideHd){
36310                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36311                 this.autoHideHd = {
36312                     "mouseout": function(e){
36313                         if(!e.within(this.el, true)){
36314                             st.delay(500);
36315                         }
36316                     },
36317                     "mouseover" : function(e){
36318                         st.cancel();
36319                     },
36320                     scope : this
36321                 };
36322             }
36323             this.el.on(this.autoHideHd);
36324         }
36325     },
36326
36327     clearAutoHide : function(){
36328         if(this.autoHide !== false){
36329             this.el.un("mouseout", this.autoHideHd.mouseout);
36330             this.el.un("mouseover", this.autoHideHd.mouseover);
36331         }
36332     },
36333
36334     clearMonitor : function(){
36335         Roo.get(document).un("click", this.slideInIf, this);
36336     },
36337
36338     // these names are backwards but not changed for compat
36339     slideOut : function(){
36340         if(this.isSlid || this.el.hasActiveFx()){
36341             return;
36342         }
36343         this.isSlid = true;
36344         if(this.collapseBtn){
36345             this.collapseBtn.hide();
36346         }
36347         this.closeBtnState = this.closeBtn.getStyle('display');
36348         this.closeBtn.hide();
36349         if(this.stickBtn){
36350             this.stickBtn.show();
36351         }
36352         this.el.show();
36353         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36354         this.beforeSlide();
36355         this.el.setStyle("z-index", 10001);
36356         this.el.slideIn(this.getSlideAnchor(), {
36357             callback: function(){
36358                 this.afterSlide();
36359                 this.initAutoHide();
36360                 Roo.get(document).on("click", this.slideInIf, this);
36361                 this.fireEvent("slideshow", this);
36362             },
36363             scope: this,
36364             block: true
36365         });
36366     },
36367
36368     afterSlideIn : function(){
36369         this.clearAutoHide();
36370         this.isSlid = false;
36371         this.clearMonitor();
36372         this.el.setStyle("z-index", "");
36373         if(this.collapseBtn){
36374             this.collapseBtn.show();
36375         }
36376         this.closeBtn.setStyle('display', this.closeBtnState);
36377         if(this.stickBtn){
36378             this.stickBtn.hide();
36379         }
36380         this.fireEvent("slidehide", this);
36381     },
36382
36383     slideIn : function(cb){
36384         if(!this.isSlid || this.el.hasActiveFx()){
36385             Roo.callback(cb);
36386             return;
36387         }
36388         this.isSlid = false;
36389         this.beforeSlide();
36390         this.el.slideOut(this.getSlideAnchor(), {
36391             callback: function(){
36392                 this.el.setLeftTop(-10000, -10000);
36393                 this.afterSlide();
36394                 this.afterSlideIn();
36395                 Roo.callback(cb);
36396             },
36397             scope: this,
36398             block: true
36399         });
36400     },
36401     
36402     slideInIf : function(e){
36403         if(!e.within(this.el)){
36404             this.slideIn();
36405         }
36406     },
36407
36408     animateCollapse : function(){
36409         this.beforeSlide();
36410         this.el.setStyle("z-index", 20000);
36411         var anchor = this.getSlideAnchor();
36412         this.el.slideOut(anchor, {
36413             callback : function(){
36414                 this.el.setStyle("z-index", "");
36415                 this.collapsedEl.slideIn(anchor, {duration:.3});
36416                 this.afterSlide();
36417                 this.el.setLocation(-10000,-10000);
36418                 this.el.hide();
36419                 this.fireEvent("collapsed", this);
36420             },
36421             scope: this,
36422             block: true
36423         });
36424     },
36425
36426     animateExpand : function(){
36427         this.beforeSlide();
36428         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36429         this.el.setStyle("z-index", 20000);
36430         this.collapsedEl.hide({
36431             duration:.1
36432         });
36433         this.el.slideIn(this.getSlideAnchor(), {
36434             callback : function(){
36435                 this.el.setStyle("z-index", "");
36436                 this.afterSlide();
36437                 if(this.split){
36438                     this.split.el.show();
36439                 }
36440                 this.fireEvent("invalidated", this);
36441                 this.fireEvent("expanded", this);
36442             },
36443             scope: this,
36444             block: true
36445         });
36446     },
36447
36448     anchors : {
36449         "west" : "left",
36450         "east" : "right",
36451         "north" : "top",
36452         "south" : "bottom"
36453     },
36454
36455     sanchors : {
36456         "west" : "l",
36457         "east" : "r",
36458         "north" : "t",
36459         "south" : "b"
36460     },
36461
36462     canchors : {
36463         "west" : "tl-tr",
36464         "east" : "tr-tl",
36465         "north" : "tl-bl",
36466         "south" : "bl-tl"
36467     },
36468
36469     getAnchor : function(){
36470         return this.anchors[this.position];
36471     },
36472
36473     getCollapseAnchor : function(){
36474         return this.canchors[this.position];
36475     },
36476
36477     getSlideAnchor : function(){
36478         return this.sanchors[this.position];
36479     },
36480
36481     getAlignAdj : function(){
36482         var cm = this.cmargins;
36483         switch(this.position){
36484             case "west":
36485                 return [0, 0];
36486             break;
36487             case "east":
36488                 return [0, 0];
36489             break;
36490             case "north":
36491                 return [0, 0];
36492             break;
36493             case "south":
36494                 return [0, 0];
36495             break;
36496         }
36497     },
36498
36499     getExpandAdj : function(){
36500         var c = this.collapsedEl, cm = this.cmargins;
36501         switch(this.position){
36502             case "west":
36503                 return [-(cm.right+c.getWidth()+cm.left), 0];
36504             break;
36505             case "east":
36506                 return [cm.right+c.getWidth()+cm.left, 0];
36507             break;
36508             case "north":
36509                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36510             break;
36511             case "south":
36512                 return [0, cm.top+cm.bottom+c.getHeight()];
36513             break;
36514         }
36515     }
36516 });/*
36517  * Based on:
36518  * Ext JS Library 1.1.1
36519  * Copyright(c) 2006-2007, Ext JS, LLC.
36520  *
36521  * Originally Released Under LGPL - original licence link has changed is not relivant.
36522  *
36523  * Fork - LGPL
36524  * <script type="text/javascript">
36525  */
36526 /*
36527  * These classes are private internal classes
36528  */
36529 Roo.bootstrap.layout.Center = function(config){
36530     config.region = "center";
36531     Roo.bootstrap.layout.Region.call(this, config);
36532     this.visible = true;
36533     this.minWidth = config.minWidth || 20;
36534     this.minHeight = config.minHeight || 20;
36535 };
36536
36537 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36538     hide : function(){
36539         // center panel can't be hidden
36540     },
36541     
36542     show : function(){
36543         // center panel can't be hidden
36544     },
36545     
36546     getMinWidth: function(){
36547         return this.minWidth;
36548     },
36549     
36550     getMinHeight: function(){
36551         return this.minHeight;
36552     }
36553 });
36554
36555
36556
36557
36558  
36559
36560
36561
36562
36563
36564 Roo.bootstrap.layout.North = function(config)
36565 {
36566     config.region = 'north';
36567     config.cursor = 'n-resize';
36568     
36569     Roo.bootstrap.layout.Split.call(this, config);
36570     
36571     
36572     if(this.split){
36573         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36574         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36575         this.split.el.addClass("roo-layout-split-v");
36576     }
36577     var size = config.initialSize || config.height;
36578     if(typeof size != "undefined"){
36579         this.el.setHeight(size);
36580     }
36581 };
36582 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36583 {
36584     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36585     
36586     
36587     
36588     getBox : function(){
36589         if(this.collapsed){
36590             return this.collapsedEl.getBox();
36591         }
36592         var box = this.el.getBox();
36593         if(this.split){
36594             box.height += this.split.el.getHeight();
36595         }
36596         return box;
36597     },
36598     
36599     updateBox : function(box){
36600         if(this.split && !this.collapsed){
36601             box.height -= this.split.el.getHeight();
36602             this.split.el.setLeft(box.x);
36603             this.split.el.setTop(box.y+box.height);
36604             this.split.el.setWidth(box.width);
36605         }
36606         if(this.collapsed){
36607             this.updateBody(box.width, null);
36608         }
36609         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36610     }
36611 });
36612
36613
36614
36615
36616
36617 Roo.bootstrap.layout.South = function(config){
36618     config.region = 'south';
36619     config.cursor = 's-resize';
36620     Roo.bootstrap.layout.Split.call(this, config);
36621     if(this.split){
36622         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36623         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36624         this.split.el.addClass("roo-layout-split-v");
36625     }
36626     var size = config.initialSize || config.height;
36627     if(typeof size != "undefined"){
36628         this.el.setHeight(size);
36629     }
36630 };
36631
36632 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36633     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36634     getBox : function(){
36635         if(this.collapsed){
36636             return this.collapsedEl.getBox();
36637         }
36638         var box = this.el.getBox();
36639         if(this.split){
36640             var sh = this.split.el.getHeight();
36641             box.height += sh;
36642             box.y -= sh;
36643         }
36644         return box;
36645     },
36646     
36647     updateBox : function(box){
36648         if(this.split && !this.collapsed){
36649             var sh = this.split.el.getHeight();
36650             box.height -= sh;
36651             box.y += sh;
36652             this.split.el.setLeft(box.x);
36653             this.split.el.setTop(box.y-sh);
36654             this.split.el.setWidth(box.width);
36655         }
36656         if(this.collapsed){
36657             this.updateBody(box.width, null);
36658         }
36659         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36660     }
36661 });
36662
36663 Roo.bootstrap.layout.East = function(config){
36664     config.region = "east";
36665     config.cursor = "e-resize";
36666     Roo.bootstrap.layout.Split.call(this, config);
36667     if(this.split){
36668         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36669         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36670         this.split.el.addClass("roo-layout-split-h");
36671     }
36672     var size = config.initialSize || config.width;
36673     if(typeof size != "undefined"){
36674         this.el.setWidth(size);
36675     }
36676 };
36677 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36678     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36679     getBox : function(){
36680         if(this.collapsed){
36681             return this.collapsedEl.getBox();
36682         }
36683         var box = this.el.getBox();
36684         if(this.split){
36685             var sw = this.split.el.getWidth();
36686             box.width += sw;
36687             box.x -= sw;
36688         }
36689         return box;
36690     },
36691
36692     updateBox : function(box){
36693         if(this.split && !this.collapsed){
36694             var sw = this.split.el.getWidth();
36695             box.width -= sw;
36696             this.split.el.setLeft(box.x);
36697             this.split.el.setTop(box.y);
36698             this.split.el.setHeight(box.height);
36699             box.x += sw;
36700         }
36701         if(this.collapsed){
36702             this.updateBody(null, box.height);
36703         }
36704         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36705     }
36706 });
36707
36708 Roo.bootstrap.layout.West = function(config){
36709     config.region = "west";
36710     config.cursor = "w-resize";
36711     
36712     Roo.bootstrap.layout.Split.call(this, config);
36713     if(this.split){
36714         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36715         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36716         this.split.el.addClass("roo-layout-split-h");
36717     }
36718     
36719 };
36720 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36721     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36722     
36723     onRender: function(ctr, pos)
36724     {
36725         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36726         var size = this.config.initialSize || this.config.width;
36727         if(typeof size != "undefined"){
36728             this.el.setWidth(size);
36729         }
36730     },
36731     
36732     getBox : function(){
36733         if(this.collapsed){
36734             return this.collapsedEl.getBox();
36735         }
36736         var box = this.el.getBox();
36737         if(this.split){
36738             box.width += this.split.el.getWidth();
36739         }
36740         return box;
36741     },
36742     
36743     updateBox : function(box){
36744         if(this.split && !this.collapsed){
36745             var sw = this.split.el.getWidth();
36746             box.width -= sw;
36747             this.split.el.setLeft(box.x+box.width);
36748             this.split.el.setTop(box.y);
36749             this.split.el.setHeight(box.height);
36750         }
36751         if(this.collapsed){
36752             this.updateBody(null, box.height);
36753         }
36754         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36755     }
36756 });
36757 Roo.namespace("Roo.bootstrap.panel");/*
36758  * Based on:
36759  * Ext JS Library 1.1.1
36760  * Copyright(c) 2006-2007, Ext JS, LLC.
36761  *
36762  * Originally Released Under LGPL - original licence link has changed is not relivant.
36763  *
36764  * Fork - LGPL
36765  * <script type="text/javascript">
36766  */
36767 /**
36768  * @class Roo.ContentPanel
36769  * @extends Roo.util.Observable
36770  * A basic ContentPanel element.
36771  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36772  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36773  * @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
36774  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36775  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36776  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36777  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36778  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36779  * @cfg {String} title          The title for this panel
36780  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36781  * @cfg {String} url            Calls {@link #setUrl} with this value
36782  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36783  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36784  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36785  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36786  * @cfg {Boolean} badges render the badges
36787
36788  * @constructor
36789  * Create a new ContentPanel.
36790  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36791  * @param {String/Object} config A string to set only the title or a config object
36792  * @param {String} content (optional) Set the HTML content for this panel
36793  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36794  */
36795 Roo.bootstrap.panel.Content = function( config){
36796     
36797     this.tpl = config.tpl || false;
36798     
36799     var el = config.el;
36800     var content = config.content;
36801
36802     if(config.autoCreate){ // xtype is available if this is called from factory
36803         el = Roo.id();
36804     }
36805     this.el = Roo.get(el);
36806     if(!this.el && config && config.autoCreate){
36807         if(typeof config.autoCreate == "object"){
36808             if(!config.autoCreate.id){
36809                 config.autoCreate.id = config.id||el;
36810             }
36811             this.el = Roo.DomHelper.append(document.body,
36812                         config.autoCreate, true);
36813         }else{
36814             var elcfg =  {   tag: "div",
36815                             cls: "roo-layout-inactive-content",
36816                             id: config.id||el
36817                             };
36818             if (config.html) {
36819                 elcfg.html = config.html;
36820                 
36821             }
36822                         
36823             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36824         }
36825     } 
36826     this.closable = false;
36827     this.loaded = false;
36828     this.active = false;
36829    
36830       
36831     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36832         
36833         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36834         
36835         this.wrapEl = this.el; //this.el.wrap();
36836         var ti = [];
36837         if (config.toolbar.items) {
36838             ti = config.toolbar.items ;
36839             delete config.toolbar.items ;
36840         }
36841         
36842         var nitems = [];
36843         this.toolbar.render(this.wrapEl, 'before');
36844         for(var i =0;i < ti.length;i++) {
36845           //  Roo.log(['add child', items[i]]);
36846             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36847         }
36848         this.toolbar.items = nitems;
36849         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36850         delete config.toolbar;
36851         
36852     }
36853     /*
36854     // xtype created footer. - not sure if will work as we normally have to render first..
36855     if (this.footer && !this.footer.el && this.footer.xtype) {
36856         if (!this.wrapEl) {
36857             this.wrapEl = this.el.wrap();
36858         }
36859     
36860         this.footer.container = this.wrapEl.createChild();
36861          
36862         this.footer = Roo.factory(this.footer, Roo);
36863         
36864     }
36865     */
36866     
36867      if(typeof config == "string"){
36868         this.title = config;
36869     }else{
36870         Roo.apply(this, config);
36871     }
36872     
36873     if(this.resizeEl){
36874         this.resizeEl = Roo.get(this.resizeEl, true);
36875     }else{
36876         this.resizeEl = this.el;
36877     }
36878     // handle view.xtype
36879     
36880  
36881     
36882     
36883     this.addEvents({
36884         /**
36885          * @event activate
36886          * Fires when this panel is activated. 
36887          * @param {Roo.ContentPanel} this
36888          */
36889         "activate" : true,
36890         /**
36891          * @event deactivate
36892          * Fires when this panel is activated. 
36893          * @param {Roo.ContentPanel} this
36894          */
36895         "deactivate" : true,
36896
36897         /**
36898          * @event resize
36899          * Fires when this panel is resized if fitToFrame is true.
36900          * @param {Roo.ContentPanel} this
36901          * @param {Number} width The width after any component adjustments
36902          * @param {Number} height The height after any component adjustments
36903          */
36904         "resize" : true,
36905         
36906          /**
36907          * @event render
36908          * Fires when this tab is created
36909          * @param {Roo.ContentPanel} this
36910          */
36911         "render" : true
36912         
36913         
36914         
36915     });
36916     
36917
36918     
36919     
36920     if(this.autoScroll){
36921         this.resizeEl.setStyle("overflow", "auto");
36922     } else {
36923         // fix randome scrolling
36924         //this.el.on('scroll', function() {
36925         //    Roo.log('fix random scolling');
36926         //    this.scrollTo('top',0); 
36927         //});
36928     }
36929     content = content || this.content;
36930     if(content){
36931         this.setContent(content);
36932     }
36933     if(config && config.url){
36934         this.setUrl(this.url, this.params, this.loadOnce);
36935     }
36936     
36937     
36938     
36939     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36940     
36941     if (this.view && typeof(this.view.xtype) != 'undefined') {
36942         this.view.el = this.el.appendChild(document.createElement("div"));
36943         this.view = Roo.factory(this.view); 
36944         this.view.render  &&  this.view.render(false, '');  
36945     }
36946     
36947     
36948     this.fireEvent('render', this);
36949 };
36950
36951 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36952     
36953     tabTip : '',
36954     
36955     setRegion : function(region){
36956         this.region = region;
36957         this.setActiveClass(region && !this.background);
36958     },
36959     
36960     
36961     setActiveClass: function(state)
36962     {
36963         if(state){
36964            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36965            this.el.setStyle('position','relative');
36966         }else{
36967            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36968            this.el.setStyle('position', 'absolute');
36969         } 
36970     },
36971     
36972     /**
36973      * Returns the toolbar for this Panel if one was configured. 
36974      * @return {Roo.Toolbar} 
36975      */
36976     getToolbar : function(){
36977         return this.toolbar;
36978     },
36979     
36980     setActiveState : function(active)
36981     {
36982         this.active = active;
36983         this.setActiveClass(active);
36984         if(!active){
36985             if(this.fireEvent("deactivate", this) === false){
36986                 return false;
36987             }
36988             return true;
36989         }
36990         this.fireEvent("activate", this);
36991         return true;
36992     },
36993     /**
36994      * Updates this panel's element
36995      * @param {String} content The new content
36996      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36997     */
36998     setContent : function(content, loadScripts){
36999         this.el.update(content, loadScripts);
37000     },
37001
37002     ignoreResize : function(w, h){
37003         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37004             return true;
37005         }else{
37006             this.lastSize = {width: w, height: h};
37007             return false;
37008         }
37009     },
37010     /**
37011      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37012      * @return {Roo.UpdateManager} The UpdateManager
37013      */
37014     getUpdateManager : function(){
37015         return this.el.getUpdateManager();
37016     },
37017      /**
37018      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37019      * @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:
37020 <pre><code>
37021 panel.load({
37022     url: "your-url.php",
37023     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37024     callback: yourFunction,
37025     scope: yourObject, //(optional scope)
37026     discardUrl: false,
37027     nocache: false,
37028     text: "Loading...",
37029     timeout: 30,
37030     scripts: false
37031 });
37032 </code></pre>
37033      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37034      * 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.
37035      * @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}
37036      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37037      * @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.
37038      * @return {Roo.ContentPanel} this
37039      */
37040     load : function(){
37041         var um = this.el.getUpdateManager();
37042         um.update.apply(um, arguments);
37043         return this;
37044     },
37045
37046
37047     /**
37048      * 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.
37049      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37050      * @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)
37051      * @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)
37052      * @return {Roo.UpdateManager} The UpdateManager
37053      */
37054     setUrl : function(url, params, loadOnce){
37055         if(this.refreshDelegate){
37056             this.removeListener("activate", this.refreshDelegate);
37057         }
37058         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37059         this.on("activate", this.refreshDelegate);
37060         return this.el.getUpdateManager();
37061     },
37062     
37063     _handleRefresh : function(url, params, loadOnce){
37064         if(!loadOnce || !this.loaded){
37065             var updater = this.el.getUpdateManager();
37066             updater.update(url, params, this._setLoaded.createDelegate(this));
37067         }
37068     },
37069     
37070     _setLoaded : function(){
37071         this.loaded = true;
37072     }, 
37073     
37074     /**
37075      * Returns this panel's id
37076      * @return {String} 
37077      */
37078     getId : function(){
37079         return this.el.id;
37080     },
37081     
37082     /** 
37083      * Returns this panel's element - used by regiosn to add.
37084      * @return {Roo.Element} 
37085      */
37086     getEl : function(){
37087         return this.wrapEl || this.el;
37088     },
37089     
37090    
37091     
37092     adjustForComponents : function(width, height)
37093     {
37094         //Roo.log('adjustForComponents ');
37095         if(this.resizeEl != this.el){
37096             width -= this.el.getFrameWidth('lr');
37097             height -= this.el.getFrameWidth('tb');
37098         }
37099         if(this.toolbar){
37100             var te = this.toolbar.getEl();
37101             te.setWidth(width);
37102             height -= te.getHeight();
37103         }
37104         if(this.footer){
37105             var te = this.footer.getEl();
37106             te.setWidth(width);
37107             height -= te.getHeight();
37108         }
37109         
37110         
37111         if(this.adjustments){
37112             width += this.adjustments[0];
37113             height += this.adjustments[1];
37114         }
37115         return {"width": width, "height": height};
37116     },
37117     
37118     setSize : function(width, height){
37119         if(this.fitToFrame && !this.ignoreResize(width, height)){
37120             if(this.fitContainer && this.resizeEl != this.el){
37121                 this.el.setSize(width, height);
37122             }
37123             var size = this.adjustForComponents(width, height);
37124             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37125             this.fireEvent('resize', this, size.width, size.height);
37126         }
37127     },
37128     
37129     /**
37130      * Returns this panel's title
37131      * @return {String} 
37132      */
37133     getTitle : function(){
37134         
37135         if (typeof(this.title) != 'object') {
37136             return this.title;
37137         }
37138         
37139         var t = '';
37140         for (var k in this.title) {
37141             if (!this.title.hasOwnProperty(k)) {
37142                 continue;
37143             }
37144             
37145             if (k.indexOf('-') >= 0) {
37146                 var s = k.split('-');
37147                 for (var i = 0; i<s.length; i++) {
37148                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37149                 }
37150             } else {
37151                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37152             }
37153         }
37154         return t;
37155     },
37156     
37157     /**
37158      * Set this panel's title
37159      * @param {String} title
37160      */
37161     setTitle : function(title){
37162         this.title = title;
37163         if(this.region){
37164             this.region.updatePanelTitle(this, title);
37165         }
37166     },
37167     
37168     /**
37169      * Returns true is this panel was configured to be closable
37170      * @return {Boolean} 
37171      */
37172     isClosable : function(){
37173         return this.closable;
37174     },
37175     
37176     beforeSlide : function(){
37177         this.el.clip();
37178         this.resizeEl.clip();
37179     },
37180     
37181     afterSlide : function(){
37182         this.el.unclip();
37183         this.resizeEl.unclip();
37184     },
37185     
37186     /**
37187      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37188      *   Will fail silently if the {@link #setUrl} method has not been called.
37189      *   This does not activate the panel, just updates its content.
37190      */
37191     refresh : function(){
37192         if(this.refreshDelegate){
37193            this.loaded = false;
37194            this.refreshDelegate();
37195         }
37196     },
37197     
37198     /**
37199      * Destroys this panel
37200      */
37201     destroy : function(){
37202         this.el.removeAllListeners();
37203         var tempEl = document.createElement("span");
37204         tempEl.appendChild(this.el.dom);
37205         tempEl.innerHTML = "";
37206         this.el.remove();
37207         this.el = null;
37208     },
37209     
37210     /**
37211      * form - if the content panel contains a form - this is a reference to it.
37212      * @type {Roo.form.Form}
37213      */
37214     form : false,
37215     /**
37216      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37217      *    This contains a reference to it.
37218      * @type {Roo.View}
37219      */
37220     view : false,
37221     
37222       /**
37223      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37224      * <pre><code>
37225
37226 layout.addxtype({
37227        xtype : 'Form',
37228        items: [ .... ]
37229    }
37230 );
37231
37232 </code></pre>
37233      * @param {Object} cfg Xtype definition of item to add.
37234      */
37235     
37236     
37237     getChildContainer: function () {
37238         return this.getEl();
37239     }
37240     
37241     
37242     /*
37243         var  ret = new Roo.factory(cfg);
37244         return ret;
37245         
37246         
37247         // add form..
37248         if (cfg.xtype.match(/^Form$/)) {
37249             
37250             var el;
37251             //if (this.footer) {
37252             //    el = this.footer.container.insertSibling(false, 'before');
37253             //} else {
37254                 el = this.el.createChild();
37255             //}
37256
37257             this.form = new  Roo.form.Form(cfg);
37258             
37259             
37260             if ( this.form.allItems.length) {
37261                 this.form.render(el.dom);
37262             }
37263             return this.form;
37264         }
37265         // should only have one of theses..
37266         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37267             // views.. should not be just added - used named prop 'view''
37268             
37269             cfg.el = this.el.appendChild(document.createElement("div"));
37270             // factory?
37271             
37272             var ret = new Roo.factory(cfg);
37273              
37274              ret.render && ret.render(false, ''); // render blank..
37275             this.view = ret;
37276             return ret;
37277         }
37278         return false;
37279     }
37280     \*/
37281 });
37282  
37283 /**
37284  * @class Roo.bootstrap.panel.Grid
37285  * @extends Roo.bootstrap.panel.Content
37286  * @constructor
37287  * Create a new GridPanel.
37288  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37289  * @param {Object} config A the config object
37290   
37291  */
37292
37293
37294
37295 Roo.bootstrap.panel.Grid = function(config)
37296 {
37297     
37298       
37299     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37300         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37301
37302     config.el = this.wrapper;
37303     //this.el = this.wrapper;
37304     
37305       if (config.container) {
37306         // ctor'ed from a Border/panel.grid
37307         
37308         
37309         this.wrapper.setStyle("overflow", "hidden");
37310         this.wrapper.addClass('roo-grid-container');
37311
37312     }
37313     
37314     
37315     if(config.toolbar){
37316         var tool_el = this.wrapper.createChild();    
37317         this.toolbar = Roo.factory(config.toolbar);
37318         var ti = [];
37319         if (config.toolbar.items) {
37320             ti = config.toolbar.items ;
37321             delete config.toolbar.items ;
37322         }
37323         
37324         var nitems = [];
37325         this.toolbar.render(tool_el);
37326         for(var i =0;i < ti.length;i++) {
37327           //  Roo.log(['add child', items[i]]);
37328             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37329         }
37330         this.toolbar.items = nitems;
37331         
37332         delete config.toolbar;
37333     }
37334     
37335     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37336     config.grid.scrollBody = true;;
37337     config.grid.monitorWindowResize = false; // turn off autosizing
37338     config.grid.autoHeight = false;
37339     config.grid.autoWidth = false;
37340     
37341     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37342     
37343     if (config.background) {
37344         // render grid on panel activation (if panel background)
37345         this.on('activate', function(gp) {
37346             if (!gp.grid.rendered) {
37347                 gp.grid.render(this.wrapper);
37348                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37349             }
37350         });
37351             
37352     } else {
37353         this.grid.render(this.wrapper);
37354         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37355
37356     }
37357     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37358     // ??? needed ??? config.el = this.wrapper;
37359     
37360     
37361     
37362   
37363     // xtype created footer. - not sure if will work as we normally have to render first..
37364     if (this.footer && !this.footer.el && this.footer.xtype) {
37365         
37366         var ctr = this.grid.getView().getFooterPanel(true);
37367         this.footer.dataSource = this.grid.dataSource;
37368         this.footer = Roo.factory(this.footer, Roo);
37369         this.footer.render(ctr);
37370         
37371     }
37372     
37373     
37374     
37375     
37376      
37377 };
37378
37379 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37380     getId : function(){
37381         return this.grid.id;
37382     },
37383     
37384     /**
37385      * Returns the grid for this panel
37386      * @return {Roo.bootstrap.Table} 
37387      */
37388     getGrid : function(){
37389         return this.grid;    
37390     },
37391     
37392     setSize : function(width, height){
37393         if(!this.ignoreResize(width, height)){
37394             var grid = this.grid;
37395             var size = this.adjustForComponents(width, height);
37396             var gridel = grid.getGridEl();
37397             gridel.setSize(size.width, size.height);
37398             /*
37399             var thd = grid.getGridEl().select('thead',true).first();
37400             var tbd = grid.getGridEl().select('tbody', true).first();
37401             if (tbd) {
37402                 tbd.setSize(width, height - thd.getHeight());
37403             }
37404             */
37405             grid.autoSize();
37406         }
37407     },
37408      
37409     
37410     
37411     beforeSlide : function(){
37412         this.grid.getView().scroller.clip();
37413     },
37414     
37415     afterSlide : function(){
37416         this.grid.getView().scroller.unclip();
37417     },
37418     
37419     destroy : function(){
37420         this.grid.destroy();
37421         delete this.grid;
37422         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37423     }
37424 });
37425
37426 /**
37427  * @class Roo.bootstrap.panel.Nest
37428  * @extends Roo.bootstrap.panel.Content
37429  * @constructor
37430  * Create a new Panel, that can contain a layout.Border.
37431  * 
37432  * 
37433  * @param {Roo.BorderLayout} layout The layout for this panel
37434  * @param {String/Object} config A string to set only the title or a config object
37435  */
37436 Roo.bootstrap.panel.Nest = function(config)
37437 {
37438     // construct with only one argument..
37439     /* FIXME - implement nicer consturctors
37440     if (layout.layout) {
37441         config = layout;
37442         layout = config.layout;
37443         delete config.layout;
37444     }
37445     if (layout.xtype && !layout.getEl) {
37446         // then layout needs constructing..
37447         layout = Roo.factory(layout, Roo);
37448     }
37449     */
37450     
37451     config.el =  config.layout.getEl();
37452     
37453     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37454     
37455     config.layout.monitorWindowResize = false; // turn off autosizing
37456     this.layout = config.layout;
37457     this.layout.getEl().addClass("roo-layout-nested-layout");
37458     
37459     
37460     
37461     
37462 };
37463
37464 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37465
37466     setSize : function(width, height){
37467         if(!this.ignoreResize(width, height)){
37468             var size = this.adjustForComponents(width, height);
37469             var el = this.layout.getEl();
37470             if (size.height < 1) {
37471                 el.setWidth(size.width);   
37472             } else {
37473                 el.setSize(size.width, size.height);
37474             }
37475             var touch = el.dom.offsetWidth;
37476             this.layout.layout();
37477             // ie requires a double layout on the first pass
37478             if(Roo.isIE && !this.initialized){
37479                 this.initialized = true;
37480                 this.layout.layout();
37481             }
37482         }
37483     },
37484     
37485     // activate all subpanels if not currently active..
37486     
37487     setActiveState : function(active){
37488         this.active = active;
37489         this.setActiveClass(active);
37490         
37491         if(!active){
37492             this.fireEvent("deactivate", this);
37493             return;
37494         }
37495         
37496         this.fireEvent("activate", this);
37497         // not sure if this should happen before or after..
37498         if (!this.layout) {
37499             return; // should not happen..
37500         }
37501         var reg = false;
37502         for (var r in this.layout.regions) {
37503             reg = this.layout.getRegion(r);
37504             if (reg.getActivePanel()) {
37505                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37506                 reg.setActivePanel(reg.getActivePanel());
37507                 continue;
37508             }
37509             if (!reg.panels.length) {
37510                 continue;
37511             }
37512             reg.showPanel(reg.getPanel(0));
37513         }
37514         
37515         
37516         
37517         
37518     },
37519     
37520     /**
37521      * Returns the nested BorderLayout for this panel
37522      * @return {Roo.BorderLayout} 
37523      */
37524     getLayout : function(){
37525         return this.layout;
37526     },
37527     
37528      /**
37529      * Adds a xtype elements to the layout of the nested panel
37530      * <pre><code>
37531
37532 panel.addxtype({
37533        xtype : 'ContentPanel',
37534        region: 'west',
37535        items: [ .... ]
37536    }
37537 );
37538
37539 panel.addxtype({
37540         xtype : 'NestedLayoutPanel',
37541         region: 'west',
37542         layout: {
37543            center: { },
37544            west: { }   
37545         },
37546         items : [ ... list of content panels or nested layout panels.. ]
37547    }
37548 );
37549 </code></pre>
37550      * @param {Object} cfg Xtype definition of item to add.
37551      */
37552     addxtype : function(cfg) {
37553         return this.layout.addxtype(cfg);
37554     
37555     }
37556 });        /*
37557  * Based on:
37558  * Ext JS Library 1.1.1
37559  * Copyright(c) 2006-2007, Ext JS, LLC.
37560  *
37561  * Originally Released Under LGPL - original licence link has changed is not relivant.
37562  *
37563  * Fork - LGPL
37564  * <script type="text/javascript">
37565  */
37566 /**
37567  * @class Roo.TabPanel
37568  * @extends Roo.util.Observable
37569  * A lightweight tab container.
37570  * <br><br>
37571  * Usage:
37572  * <pre><code>
37573 // basic tabs 1, built from existing content
37574 var tabs = new Roo.TabPanel("tabs1");
37575 tabs.addTab("script", "View Script");
37576 tabs.addTab("markup", "View Markup");
37577 tabs.activate("script");
37578
37579 // more advanced tabs, built from javascript
37580 var jtabs = new Roo.TabPanel("jtabs");
37581 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37582
37583 // set up the UpdateManager
37584 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37585 var updater = tab2.getUpdateManager();
37586 updater.setDefaultUrl("ajax1.htm");
37587 tab2.on('activate', updater.refresh, updater, true);
37588
37589 // Use setUrl for Ajax loading
37590 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37591 tab3.setUrl("ajax2.htm", null, true);
37592
37593 // Disabled tab
37594 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37595 tab4.disable();
37596
37597 jtabs.activate("jtabs-1");
37598  * </code></pre>
37599  * @constructor
37600  * Create a new TabPanel.
37601  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37602  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37603  */
37604 Roo.bootstrap.panel.Tabs = function(config){
37605     /**
37606     * The container element for this TabPanel.
37607     * @type Roo.Element
37608     */
37609     this.el = Roo.get(config.el);
37610     delete config.el;
37611     if(config){
37612         if(typeof config == "boolean"){
37613             this.tabPosition = config ? "bottom" : "top";
37614         }else{
37615             Roo.apply(this, config);
37616         }
37617     }
37618     
37619     if(this.tabPosition == "bottom"){
37620         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37621         this.el.addClass("roo-tabs-bottom");
37622     }
37623     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37624     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37625     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37626     if(Roo.isIE){
37627         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37628     }
37629     if(this.tabPosition != "bottom"){
37630         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37631          * @type Roo.Element
37632          */
37633         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37634         this.el.addClass("roo-tabs-top");
37635     }
37636     this.items = [];
37637
37638     this.bodyEl.setStyle("position", "relative");
37639
37640     this.active = null;
37641     this.activateDelegate = this.activate.createDelegate(this);
37642
37643     this.addEvents({
37644         /**
37645          * @event tabchange
37646          * Fires when the active tab changes
37647          * @param {Roo.TabPanel} this
37648          * @param {Roo.TabPanelItem} activePanel The new active tab
37649          */
37650         "tabchange": true,
37651         /**
37652          * @event beforetabchange
37653          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37654          * @param {Roo.TabPanel} this
37655          * @param {Object} e Set cancel to true on this object to cancel the tab change
37656          * @param {Roo.TabPanelItem} tab The tab being changed to
37657          */
37658         "beforetabchange" : true
37659     });
37660
37661     Roo.EventManager.onWindowResize(this.onResize, this);
37662     this.cpad = this.el.getPadding("lr");
37663     this.hiddenCount = 0;
37664
37665
37666     // toolbar on the tabbar support...
37667     if (this.toolbar) {
37668         alert("no toolbar support yet");
37669         this.toolbar  = false;
37670         /*
37671         var tcfg = this.toolbar;
37672         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37673         this.toolbar = new Roo.Toolbar(tcfg);
37674         if (Roo.isSafari) {
37675             var tbl = tcfg.container.child('table', true);
37676             tbl.setAttribute('width', '100%');
37677         }
37678         */
37679         
37680     }
37681    
37682
37683
37684     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37685 };
37686
37687 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37688     /*
37689      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37690      */
37691     tabPosition : "top",
37692     /*
37693      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37694      */
37695     currentTabWidth : 0,
37696     /*
37697      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37698      */
37699     minTabWidth : 40,
37700     /*
37701      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37702      */
37703     maxTabWidth : 250,
37704     /*
37705      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37706      */
37707     preferredTabWidth : 175,
37708     /*
37709      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37710      */
37711     resizeTabs : false,
37712     /*
37713      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37714      */
37715     monitorResize : true,
37716     /*
37717      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37718      */
37719     toolbar : false,
37720
37721     /**
37722      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37723      * @param {String} id The id of the div to use <b>or create</b>
37724      * @param {String} text The text for the tab
37725      * @param {String} content (optional) Content to put in the TabPanelItem body
37726      * @param {Boolean} closable (optional) True to create a close icon on the tab
37727      * @return {Roo.TabPanelItem} The created TabPanelItem
37728      */
37729     addTab : function(id, text, content, closable, tpl)
37730     {
37731         var item = new Roo.bootstrap.panel.TabItem({
37732             panel: this,
37733             id : id,
37734             text : text,
37735             closable : closable,
37736             tpl : tpl
37737         });
37738         this.addTabItem(item);
37739         if(content){
37740             item.setContent(content);
37741         }
37742         return item;
37743     },
37744
37745     /**
37746      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37747      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37748      * @return {Roo.TabPanelItem}
37749      */
37750     getTab : function(id){
37751         return this.items[id];
37752     },
37753
37754     /**
37755      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37756      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37757      */
37758     hideTab : function(id){
37759         var t = this.items[id];
37760         if(!t.isHidden()){
37761            t.setHidden(true);
37762            this.hiddenCount++;
37763            this.autoSizeTabs();
37764         }
37765     },
37766
37767     /**
37768      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37769      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37770      */
37771     unhideTab : function(id){
37772         var t = this.items[id];
37773         if(t.isHidden()){
37774            t.setHidden(false);
37775            this.hiddenCount--;
37776            this.autoSizeTabs();
37777         }
37778     },
37779
37780     /**
37781      * Adds an existing {@link Roo.TabPanelItem}.
37782      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37783      */
37784     addTabItem : function(item){
37785         this.items[item.id] = item;
37786         this.items.push(item);
37787       //  if(this.resizeTabs){
37788     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37789   //         this.autoSizeTabs();
37790 //        }else{
37791 //            item.autoSize();
37792        // }
37793     },
37794
37795     /**
37796      * Removes a {@link Roo.TabPanelItem}.
37797      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37798      */
37799     removeTab : function(id){
37800         var items = this.items;
37801         var tab = items[id];
37802         if(!tab) { return; }
37803         var index = items.indexOf(tab);
37804         if(this.active == tab && items.length > 1){
37805             var newTab = this.getNextAvailable(index);
37806             if(newTab) {
37807                 newTab.activate();
37808             }
37809         }
37810         this.stripEl.dom.removeChild(tab.pnode.dom);
37811         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37812             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37813         }
37814         items.splice(index, 1);
37815         delete this.items[tab.id];
37816         tab.fireEvent("close", tab);
37817         tab.purgeListeners();
37818         this.autoSizeTabs();
37819     },
37820
37821     getNextAvailable : function(start){
37822         var items = this.items;
37823         var index = start;
37824         // look for a next tab that will slide over to
37825         // replace the one being removed
37826         while(index < items.length){
37827             var item = items[++index];
37828             if(item && !item.isHidden()){
37829                 return item;
37830             }
37831         }
37832         // if one isn't found select the previous tab (on the left)
37833         index = start;
37834         while(index >= 0){
37835             var item = items[--index];
37836             if(item && !item.isHidden()){
37837                 return item;
37838             }
37839         }
37840         return null;
37841     },
37842
37843     /**
37844      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37845      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37846      */
37847     disableTab : function(id){
37848         var tab = this.items[id];
37849         if(tab && this.active != tab){
37850             tab.disable();
37851         }
37852     },
37853
37854     /**
37855      * Enables a {@link Roo.TabPanelItem} that is disabled.
37856      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37857      */
37858     enableTab : function(id){
37859         var tab = this.items[id];
37860         tab.enable();
37861     },
37862
37863     /**
37864      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37865      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37866      * @return {Roo.TabPanelItem} The TabPanelItem.
37867      */
37868     activate : function(id){
37869         var tab = this.items[id];
37870         if(!tab){
37871             return null;
37872         }
37873         if(tab == this.active || tab.disabled){
37874             return tab;
37875         }
37876         var e = {};
37877         this.fireEvent("beforetabchange", this, e, tab);
37878         if(e.cancel !== true && !tab.disabled){
37879             if(this.active){
37880                 this.active.hide();
37881             }
37882             this.active = this.items[id];
37883             this.active.show();
37884             this.fireEvent("tabchange", this, this.active);
37885         }
37886         return tab;
37887     },
37888
37889     /**
37890      * Gets the active {@link Roo.TabPanelItem}.
37891      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37892      */
37893     getActiveTab : function(){
37894         return this.active;
37895     },
37896
37897     /**
37898      * Updates the tab body element to fit the height of the container element
37899      * for overflow scrolling
37900      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37901      */
37902     syncHeight : function(targetHeight){
37903         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37904         var bm = this.bodyEl.getMargins();
37905         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37906         this.bodyEl.setHeight(newHeight);
37907         return newHeight;
37908     },
37909
37910     onResize : function(){
37911         if(this.monitorResize){
37912             this.autoSizeTabs();
37913         }
37914     },
37915
37916     /**
37917      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37918      */
37919     beginUpdate : function(){
37920         this.updating = true;
37921     },
37922
37923     /**
37924      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37925      */
37926     endUpdate : function(){
37927         this.updating = false;
37928         this.autoSizeTabs();
37929     },
37930
37931     /**
37932      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37933      */
37934     autoSizeTabs : function(){
37935         var count = this.items.length;
37936         var vcount = count - this.hiddenCount;
37937         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37938             return;
37939         }
37940         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37941         var availWidth = Math.floor(w / vcount);
37942         var b = this.stripBody;
37943         if(b.getWidth() > w){
37944             var tabs = this.items;
37945             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37946             if(availWidth < this.minTabWidth){
37947                 /*if(!this.sleft){    // incomplete scrolling code
37948                     this.createScrollButtons();
37949                 }
37950                 this.showScroll();
37951                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37952             }
37953         }else{
37954             if(this.currentTabWidth < this.preferredTabWidth){
37955                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37956             }
37957         }
37958     },
37959
37960     /**
37961      * Returns the number of tabs in this TabPanel.
37962      * @return {Number}
37963      */
37964      getCount : function(){
37965          return this.items.length;
37966      },
37967
37968     /**
37969      * Resizes all the tabs to the passed width
37970      * @param {Number} The new width
37971      */
37972     setTabWidth : function(width){
37973         this.currentTabWidth = width;
37974         for(var i = 0, len = this.items.length; i < len; i++) {
37975                 if(!this.items[i].isHidden()) {
37976                 this.items[i].setWidth(width);
37977             }
37978         }
37979     },
37980
37981     /**
37982      * Destroys this TabPanel
37983      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37984      */
37985     destroy : function(removeEl){
37986         Roo.EventManager.removeResizeListener(this.onResize, this);
37987         for(var i = 0, len = this.items.length; i < len; i++){
37988             this.items[i].purgeListeners();
37989         }
37990         if(removeEl === true){
37991             this.el.update("");
37992             this.el.remove();
37993         }
37994     },
37995     
37996     createStrip : function(container)
37997     {
37998         var strip = document.createElement("nav");
37999         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38000         container.appendChild(strip);
38001         return strip;
38002     },
38003     
38004     createStripList : function(strip)
38005     {
38006         // div wrapper for retard IE
38007         // returns the "tr" element.
38008         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38009         //'<div class="x-tabs-strip-wrap">'+
38010           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38011           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38012         return strip.firstChild; //.firstChild.firstChild.firstChild;
38013     },
38014     createBody : function(container)
38015     {
38016         var body = document.createElement("div");
38017         Roo.id(body, "tab-body");
38018         //Roo.fly(body).addClass("x-tabs-body");
38019         Roo.fly(body).addClass("tab-content");
38020         container.appendChild(body);
38021         return body;
38022     },
38023     createItemBody :function(bodyEl, id){
38024         var body = Roo.getDom(id);
38025         if(!body){
38026             body = document.createElement("div");
38027             body.id = id;
38028         }
38029         //Roo.fly(body).addClass("x-tabs-item-body");
38030         Roo.fly(body).addClass("tab-pane");
38031          bodyEl.insertBefore(body, bodyEl.firstChild);
38032         return body;
38033     },
38034     /** @private */
38035     createStripElements :  function(stripEl, text, closable, tpl)
38036     {
38037         var td = document.createElement("li"); // was td..
38038         
38039         
38040         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38041         
38042         
38043         stripEl.appendChild(td);
38044         /*if(closable){
38045             td.className = "x-tabs-closable";
38046             if(!this.closeTpl){
38047                 this.closeTpl = new Roo.Template(
38048                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38049                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38050                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38051                 );
38052             }
38053             var el = this.closeTpl.overwrite(td, {"text": text});
38054             var close = el.getElementsByTagName("div")[0];
38055             var inner = el.getElementsByTagName("em")[0];
38056             return {"el": el, "close": close, "inner": inner};
38057         } else {
38058         */
38059         // not sure what this is..
38060 //            if(!this.tabTpl){
38061                 //this.tabTpl = new Roo.Template(
38062                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38063                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38064                 //);
38065 //                this.tabTpl = new Roo.Template(
38066 //                   '<a href="#">' +
38067 //                   '<span unselectable="on"' +
38068 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38069 //                            ' >{text}</span></a>'
38070 //                );
38071 //                
38072 //            }
38073
38074
38075             var template = tpl || this.tabTpl || false;
38076             
38077             if(!template){
38078                 
38079                 template = new Roo.Template(
38080                    '<a href="#">' +
38081                    '<span unselectable="on"' +
38082                             (this.disableTooltips ? '' : ' title="{text}"') +
38083                             ' >{text}</span></a>'
38084                 );
38085             }
38086             
38087             switch (typeof(template)) {
38088                 case 'object' :
38089                     break;
38090                 case 'string' :
38091                     template = new Roo.Template(template);
38092                     break;
38093                 default :
38094                     break;
38095             }
38096             
38097             var el = template.overwrite(td, {"text": text});
38098             
38099             var inner = el.getElementsByTagName("span")[0];
38100             
38101             return {"el": el, "inner": inner};
38102             
38103     }
38104         
38105     
38106 });
38107
38108 /**
38109  * @class Roo.TabPanelItem
38110  * @extends Roo.util.Observable
38111  * Represents an individual item (tab plus body) in a TabPanel.
38112  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38113  * @param {String} id The id of this TabPanelItem
38114  * @param {String} text The text for the tab of this TabPanelItem
38115  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38116  */
38117 Roo.bootstrap.panel.TabItem = function(config){
38118     /**
38119      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38120      * @type Roo.TabPanel
38121      */
38122     this.tabPanel = config.panel;
38123     /**
38124      * The id for this TabPanelItem
38125      * @type String
38126      */
38127     this.id = config.id;
38128     /** @private */
38129     this.disabled = false;
38130     /** @private */
38131     this.text = config.text;
38132     /** @private */
38133     this.loaded = false;
38134     this.closable = config.closable;
38135
38136     /**
38137      * The body element for this TabPanelItem.
38138      * @type Roo.Element
38139      */
38140     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38141     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38142     this.bodyEl.setStyle("display", "block");
38143     this.bodyEl.setStyle("zoom", "1");
38144     //this.hideAction();
38145
38146     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38147     /** @private */
38148     this.el = Roo.get(els.el);
38149     this.inner = Roo.get(els.inner, true);
38150     this.textEl = Roo.get(this.el.dom.firstChild, true);
38151     this.pnode = Roo.get(els.el.parentNode, true);
38152 //    this.el.on("mousedown", this.onTabMouseDown, this);
38153     this.el.on("click", this.onTabClick, this);
38154     /** @private */
38155     if(config.closable){
38156         var c = Roo.get(els.close, true);
38157         c.dom.title = this.closeText;
38158         c.addClassOnOver("close-over");
38159         c.on("click", this.closeClick, this);
38160      }
38161
38162     this.addEvents({
38163          /**
38164          * @event activate
38165          * Fires when this tab becomes the active tab.
38166          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38167          * @param {Roo.TabPanelItem} this
38168          */
38169         "activate": true,
38170         /**
38171          * @event beforeclose
38172          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38173          * @param {Roo.TabPanelItem} this
38174          * @param {Object} e Set cancel to true on this object to cancel the close.
38175          */
38176         "beforeclose": true,
38177         /**
38178          * @event close
38179          * Fires when this tab is closed.
38180          * @param {Roo.TabPanelItem} this
38181          */
38182          "close": true,
38183         /**
38184          * @event deactivate
38185          * Fires when this tab is no longer the active tab.
38186          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38187          * @param {Roo.TabPanelItem} this
38188          */
38189          "deactivate" : true
38190     });
38191     this.hidden = false;
38192
38193     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38194 };
38195
38196 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38197            {
38198     purgeListeners : function(){
38199        Roo.util.Observable.prototype.purgeListeners.call(this);
38200        this.el.removeAllListeners();
38201     },
38202     /**
38203      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38204      */
38205     show : function(){
38206         this.pnode.addClass("active");
38207         this.showAction();
38208         if(Roo.isOpera){
38209             this.tabPanel.stripWrap.repaint();
38210         }
38211         this.fireEvent("activate", this.tabPanel, this);
38212     },
38213
38214     /**
38215      * Returns true if this tab is the active tab.
38216      * @return {Boolean}
38217      */
38218     isActive : function(){
38219         return this.tabPanel.getActiveTab() == this;
38220     },
38221
38222     /**
38223      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38224      */
38225     hide : function(){
38226         this.pnode.removeClass("active");
38227         this.hideAction();
38228         this.fireEvent("deactivate", this.tabPanel, this);
38229     },
38230
38231     hideAction : function(){
38232         this.bodyEl.hide();
38233         this.bodyEl.setStyle("position", "absolute");
38234         this.bodyEl.setLeft("-20000px");
38235         this.bodyEl.setTop("-20000px");
38236     },
38237
38238     showAction : function(){
38239         this.bodyEl.setStyle("position", "relative");
38240         this.bodyEl.setTop("");
38241         this.bodyEl.setLeft("");
38242         this.bodyEl.show();
38243     },
38244
38245     /**
38246      * Set the tooltip for the tab.
38247      * @param {String} tooltip The tab's tooltip
38248      */
38249     setTooltip : function(text){
38250         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38251             this.textEl.dom.qtip = text;
38252             this.textEl.dom.removeAttribute('title');
38253         }else{
38254             this.textEl.dom.title = text;
38255         }
38256     },
38257
38258     onTabClick : function(e){
38259         e.preventDefault();
38260         this.tabPanel.activate(this.id);
38261     },
38262
38263     onTabMouseDown : function(e){
38264         e.preventDefault();
38265         this.tabPanel.activate(this.id);
38266     },
38267 /*
38268     getWidth : function(){
38269         return this.inner.getWidth();
38270     },
38271
38272     setWidth : function(width){
38273         var iwidth = width - this.pnode.getPadding("lr");
38274         this.inner.setWidth(iwidth);
38275         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38276         this.pnode.setWidth(width);
38277     },
38278 */
38279     /**
38280      * Show or hide the tab
38281      * @param {Boolean} hidden True to hide or false to show.
38282      */
38283     setHidden : function(hidden){
38284         this.hidden = hidden;
38285         this.pnode.setStyle("display", hidden ? "none" : "");
38286     },
38287
38288     /**
38289      * Returns true if this tab is "hidden"
38290      * @return {Boolean}
38291      */
38292     isHidden : function(){
38293         return this.hidden;
38294     },
38295
38296     /**
38297      * Returns the text for this tab
38298      * @return {String}
38299      */
38300     getText : function(){
38301         return this.text;
38302     },
38303     /*
38304     autoSize : function(){
38305         //this.el.beginMeasure();
38306         this.textEl.setWidth(1);
38307         /*
38308          *  #2804 [new] Tabs in Roojs
38309          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38310          */
38311         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38312         //this.el.endMeasure();
38313     //},
38314
38315     /**
38316      * Sets the text for the tab (Note: this also sets the tooltip text)
38317      * @param {String} text The tab's text and tooltip
38318      */
38319     setText : function(text){
38320         this.text = text;
38321         this.textEl.update(text);
38322         this.setTooltip(text);
38323         //if(!this.tabPanel.resizeTabs){
38324         //    this.autoSize();
38325         //}
38326     },
38327     /**
38328      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38329      */
38330     activate : function(){
38331         this.tabPanel.activate(this.id);
38332     },
38333
38334     /**
38335      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38336      */
38337     disable : function(){
38338         if(this.tabPanel.active != this){
38339             this.disabled = true;
38340             this.pnode.addClass("disabled");
38341         }
38342     },
38343
38344     /**
38345      * Enables this TabPanelItem if it was previously disabled.
38346      */
38347     enable : function(){
38348         this.disabled = false;
38349         this.pnode.removeClass("disabled");
38350     },
38351
38352     /**
38353      * Sets the content for this TabPanelItem.
38354      * @param {String} content The content
38355      * @param {Boolean} loadScripts true to look for and load scripts
38356      */
38357     setContent : function(content, loadScripts){
38358         this.bodyEl.update(content, loadScripts);
38359     },
38360
38361     /**
38362      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38363      * @return {Roo.UpdateManager} The UpdateManager
38364      */
38365     getUpdateManager : function(){
38366         return this.bodyEl.getUpdateManager();
38367     },
38368
38369     /**
38370      * Set a URL to be used to load the content for this TabPanelItem.
38371      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38372      * @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)
38373      * @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)
38374      * @return {Roo.UpdateManager} The UpdateManager
38375      */
38376     setUrl : function(url, params, loadOnce){
38377         if(this.refreshDelegate){
38378             this.un('activate', this.refreshDelegate);
38379         }
38380         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38381         this.on("activate", this.refreshDelegate);
38382         return this.bodyEl.getUpdateManager();
38383     },
38384
38385     /** @private */
38386     _handleRefresh : function(url, params, loadOnce){
38387         if(!loadOnce || !this.loaded){
38388             var updater = this.bodyEl.getUpdateManager();
38389             updater.update(url, params, this._setLoaded.createDelegate(this));
38390         }
38391     },
38392
38393     /**
38394      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38395      *   Will fail silently if the setUrl method has not been called.
38396      *   This does not activate the panel, just updates its content.
38397      */
38398     refresh : function(){
38399         if(this.refreshDelegate){
38400            this.loaded = false;
38401            this.refreshDelegate();
38402         }
38403     },
38404
38405     /** @private */
38406     _setLoaded : function(){
38407         this.loaded = true;
38408     },
38409
38410     /** @private */
38411     closeClick : function(e){
38412         var o = {};
38413         e.stopEvent();
38414         this.fireEvent("beforeclose", this, o);
38415         if(o.cancel !== true){
38416             this.tabPanel.removeTab(this.id);
38417         }
38418     },
38419     /**
38420      * The text displayed in the tooltip for the close icon.
38421      * @type String
38422      */
38423     closeText : "Close this tab"
38424 });
38425 /**
38426 *    This script refer to:
38427 *    Title: International Telephone Input
38428 *    Author: Jack O'Connor
38429 *    Code version:  v12.1.12
38430 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38431 **/
38432
38433 Roo.bootstrap.PhoneInputData = function() {
38434     var d = [
38435       [
38436         "Afghanistan (‫افغانستان‬‎)",
38437         "af",
38438         "93"
38439       ],
38440       [
38441         "Albania (Shqipëri)",
38442         "al",
38443         "355"
38444       ],
38445       [
38446         "Algeria (‫الجزائر‬‎)",
38447         "dz",
38448         "213"
38449       ],
38450       [
38451         "American Samoa",
38452         "as",
38453         "1684"
38454       ],
38455       [
38456         "Andorra",
38457         "ad",
38458         "376"
38459       ],
38460       [
38461         "Angola",
38462         "ao",
38463         "244"
38464       ],
38465       [
38466         "Anguilla",
38467         "ai",
38468         "1264"
38469       ],
38470       [
38471         "Antigua and Barbuda",
38472         "ag",
38473         "1268"
38474       ],
38475       [
38476         "Argentina",
38477         "ar",
38478         "54"
38479       ],
38480       [
38481         "Armenia (Հայաստան)",
38482         "am",
38483         "374"
38484       ],
38485       [
38486         "Aruba",
38487         "aw",
38488         "297"
38489       ],
38490       [
38491         "Australia",
38492         "au",
38493         "61",
38494         0
38495       ],
38496       [
38497         "Austria (Österreich)",
38498         "at",
38499         "43"
38500       ],
38501       [
38502         "Azerbaijan (Azərbaycan)",
38503         "az",
38504         "994"
38505       ],
38506       [
38507         "Bahamas",
38508         "bs",
38509         "1242"
38510       ],
38511       [
38512         "Bahrain (‫البحرين‬‎)",
38513         "bh",
38514         "973"
38515       ],
38516       [
38517         "Bangladesh (বাংলাদেশ)",
38518         "bd",
38519         "880"
38520       ],
38521       [
38522         "Barbados",
38523         "bb",
38524         "1246"
38525       ],
38526       [
38527         "Belarus (Беларусь)",
38528         "by",
38529         "375"
38530       ],
38531       [
38532         "Belgium (België)",
38533         "be",
38534         "32"
38535       ],
38536       [
38537         "Belize",
38538         "bz",
38539         "501"
38540       ],
38541       [
38542         "Benin (Bénin)",
38543         "bj",
38544         "229"
38545       ],
38546       [
38547         "Bermuda",
38548         "bm",
38549         "1441"
38550       ],
38551       [
38552         "Bhutan (འབྲུག)",
38553         "bt",
38554         "975"
38555       ],
38556       [
38557         "Bolivia",
38558         "bo",
38559         "591"
38560       ],
38561       [
38562         "Bosnia and Herzegovina (Босна и Херцеговина)",
38563         "ba",
38564         "387"
38565       ],
38566       [
38567         "Botswana",
38568         "bw",
38569         "267"
38570       ],
38571       [
38572         "Brazil (Brasil)",
38573         "br",
38574         "55"
38575       ],
38576       [
38577         "British Indian Ocean Territory",
38578         "io",
38579         "246"
38580       ],
38581       [
38582         "British Virgin Islands",
38583         "vg",
38584         "1284"
38585       ],
38586       [
38587         "Brunei",
38588         "bn",
38589         "673"
38590       ],
38591       [
38592         "Bulgaria (България)",
38593         "bg",
38594         "359"
38595       ],
38596       [
38597         "Burkina Faso",
38598         "bf",
38599         "226"
38600       ],
38601       [
38602         "Burundi (Uburundi)",
38603         "bi",
38604         "257"
38605       ],
38606       [
38607         "Cambodia (កម្ពុជា)",
38608         "kh",
38609         "855"
38610       ],
38611       [
38612         "Cameroon (Cameroun)",
38613         "cm",
38614         "237"
38615       ],
38616       [
38617         "Canada",
38618         "ca",
38619         "1",
38620         1,
38621         ["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"]
38622       ],
38623       [
38624         "Cape Verde (Kabu Verdi)",
38625         "cv",
38626         "238"
38627       ],
38628       [
38629         "Caribbean Netherlands",
38630         "bq",
38631         "599",
38632         1
38633       ],
38634       [
38635         "Cayman Islands",
38636         "ky",
38637         "1345"
38638       ],
38639       [
38640         "Central African Republic (République centrafricaine)",
38641         "cf",
38642         "236"
38643       ],
38644       [
38645         "Chad (Tchad)",
38646         "td",
38647         "235"
38648       ],
38649       [
38650         "Chile",
38651         "cl",
38652         "56"
38653       ],
38654       [
38655         "China (中国)",
38656         "cn",
38657         "86"
38658       ],
38659       [
38660         "Christmas Island",
38661         "cx",
38662         "61",
38663         2
38664       ],
38665       [
38666         "Cocos (Keeling) Islands",
38667         "cc",
38668         "61",
38669         1
38670       ],
38671       [
38672         "Colombia",
38673         "co",
38674         "57"
38675       ],
38676       [
38677         "Comoros (‫جزر القمر‬‎)",
38678         "km",
38679         "269"
38680       ],
38681       [
38682         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38683         "cd",
38684         "243"
38685       ],
38686       [
38687         "Congo (Republic) (Congo-Brazzaville)",
38688         "cg",
38689         "242"
38690       ],
38691       [
38692         "Cook Islands",
38693         "ck",
38694         "682"
38695       ],
38696       [
38697         "Costa Rica",
38698         "cr",
38699         "506"
38700       ],
38701       [
38702         "Côte d’Ivoire",
38703         "ci",
38704         "225"
38705       ],
38706       [
38707         "Croatia (Hrvatska)",
38708         "hr",
38709         "385"
38710       ],
38711       [
38712         "Cuba",
38713         "cu",
38714         "53"
38715       ],
38716       [
38717         "Curaçao",
38718         "cw",
38719         "599",
38720         0
38721       ],
38722       [
38723         "Cyprus (Κύπρος)",
38724         "cy",
38725         "357"
38726       ],
38727       [
38728         "Czech Republic (Česká republika)",
38729         "cz",
38730         "420"
38731       ],
38732       [
38733         "Denmark (Danmark)",
38734         "dk",
38735         "45"
38736       ],
38737       [
38738         "Djibouti",
38739         "dj",
38740         "253"
38741       ],
38742       [
38743         "Dominica",
38744         "dm",
38745         "1767"
38746       ],
38747       [
38748         "Dominican Republic (República Dominicana)",
38749         "do",
38750         "1",
38751         2,
38752         ["809", "829", "849"]
38753       ],
38754       [
38755         "Ecuador",
38756         "ec",
38757         "593"
38758       ],
38759       [
38760         "Egypt (‫مصر‬‎)",
38761         "eg",
38762         "20"
38763       ],
38764       [
38765         "El Salvador",
38766         "sv",
38767         "503"
38768       ],
38769       [
38770         "Equatorial Guinea (Guinea Ecuatorial)",
38771         "gq",
38772         "240"
38773       ],
38774       [
38775         "Eritrea",
38776         "er",
38777         "291"
38778       ],
38779       [
38780         "Estonia (Eesti)",
38781         "ee",
38782         "372"
38783       ],
38784       [
38785         "Ethiopia",
38786         "et",
38787         "251"
38788       ],
38789       [
38790         "Falkland Islands (Islas Malvinas)",
38791         "fk",
38792         "500"
38793       ],
38794       [
38795         "Faroe Islands (Føroyar)",
38796         "fo",
38797         "298"
38798       ],
38799       [
38800         "Fiji",
38801         "fj",
38802         "679"
38803       ],
38804       [
38805         "Finland (Suomi)",
38806         "fi",
38807         "358",
38808         0
38809       ],
38810       [
38811         "France",
38812         "fr",
38813         "33"
38814       ],
38815       [
38816         "French Guiana (Guyane française)",
38817         "gf",
38818         "594"
38819       ],
38820       [
38821         "French Polynesia (Polynésie française)",
38822         "pf",
38823         "689"
38824       ],
38825       [
38826         "Gabon",
38827         "ga",
38828         "241"
38829       ],
38830       [
38831         "Gambia",
38832         "gm",
38833         "220"
38834       ],
38835       [
38836         "Georgia (საქართველო)",
38837         "ge",
38838         "995"
38839       ],
38840       [
38841         "Germany (Deutschland)",
38842         "de",
38843         "49"
38844       ],
38845       [
38846         "Ghana (Gaana)",
38847         "gh",
38848         "233"
38849       ],
38850       [
38851         "Gibraltar",
38852         "gi",
38853         "350"
38854       ],
38855       [
38856         "Greece (Ελλάδα)",
38857         "gr",
38858         "30"
38859       ],
38860       [
38861         "Greenland (Kalaallit Nunaat)",
38862         "gl",
38863         "299"
38864       ],
38865       [
38866         "Grenada",
38867         "gd",
38868         "1473"
38869       ],
38870       [
38871         "Guadeloupe",
38872         "gp",
38873         "590",
38874         0
38875       ],
38876       [
38877         "Guam",
38878         "gu",
38879         "1671"
38880       ],
38881       [
38882         "Guatemala",
38883         "gt",
38884         "502"
38885       ],
38886       [
38887         "Guernsey",
38888         "gg",
38889         "44",
38890         1
38891       ],
38892       [
38893         "Guinea (Guinée)",
38894         "gn",
38895         "224"
38896       ],
38897       [
38898         "Guinea-Bissau (Guiné Bissau)",
38899         "gw",
38900         "245"
38901       ],
38902       [
38903         "Guyana",
38904         "gy",
38905         "592"
38906       ],
38907       [
38908         "Haiti",
38909         "ht",
38910         "509"
38911       ],
38912       [
38913         "Honduras",
38914         "hn",
38915         "504"
38916       ],
38917       [
38918         "Hong Kong (香港)",
38919         "hk",
38920         "852"
38921       ],
38922       [
38923         "Hungary (Magyarország)",
38924         "hu",
38925         "36"
38926       ],
38927       [
38928         "Iceland (Ísland)",
38929         "is",
38930         "354"
38931       ],
38932       [
38933         "India (भारत)",
38934         "in",
38935         "91"
38936       ],
38937       [
38938         "Indonesia",
38939         "id",
38940         "62"
38941       ],
38942       [
38943         "Iran (‫ایران‬‎)",
38944         "ir",
38945         "98"
38946       ],
38947       [
38948         "Iraq (‫العراق‬‎)",
38949         "iq",
38950         "964"
38951       ],
38952       [
38953         "Ireland",
38954         "ie",
38955         "353"
38956       ],
38957       [
38958         "Isle of Man",
38959         "im",
38960         "44",
38961         2
38962       ],
38963       [
38964         "Israel (‫ישראל‬‎)",
38965         "il",
38966         "972"
38967       ],
38968       [
38969         "Italy (Italia)",
38970         "it",
38971         "39",
38972         0
38973       ],
38974       [
38975         "Jamaica",
38976         "jm",
38977         "1876"
38978       ],
38979       [
38980         "Japan (日本)",
38981         "jp",
38982         "81"
38983       ],
38984       [
38985         "Jersey",
38986         "je",
38987         "44",
38988         3
38989       ],
38990       [
38991         "Jordan (‫الأردن‬‎)",
38992         "jo",
38993         "962"
38994       ],
38995       [
38996         "Kazakhstan (Казахстан)",
38997         "kz",
38998         "7",
38999         1
39000       ],
39001       [
39002         "Kenya",
39003         "ke",
39004         "254"
39005       ],
39006       [
39007         "Kiribati",
39008         "ki",
39009         "686"
39010       ],
39011       [
39012         "Kosovo",
39013         "xk",
39014         "383"
39015       ],
39016       [
39017         "Kuwait (‫الكويت‬‎)",
39018         "kw",
39019         "965"
39020       ],
39021       [
39022         "Kyrgyzstan (Кыргызстан)",
39023         "kg",
39024         "996"
39025       ],
39026       [
39027         "Laos (ລາວ)",
39028         "la",
39029         "856"
39030       ],
39031       [
39032         "Latvia (Latvija)",
39033         "lv",
39034         "371"
39035       ],
39036       [
39037         "Lebanon (‫لبنان‬‎)",
39038         "lb",
39039         "961"
39040       ],
39041       [
39042         "Lesotho",
39043         "ls",
39044         "266"
39045       ],
39046       [
39047         "Liberia",
39048         "lr",
39049         "231"
39050       ],
39051       [
39052         "Libya (‫ليبيا‬‎)",
39053         "ly",
39054         "218"
39055       ],
39056       [
39057         "Liechtenstein",
39058         "li",
39059         "423"
39060       ],
39061       [
39062         "Lithuania (Lietuva)",
39063         "lt",
39064         "370"
39065       ],
39066       [
39067         "Luxembourg",
39068         "lu",
39069         "352"
39070       ],
39071       [
39072         "Macau (澳門)",
39073         "mo",
39074         "853"
39075       ],
39076       [
39077         "Macedonia (FYROM) (Македонија)",
39078         "mk",
39079         "389"
39080       ],
39081       [
39082         "Madagascar (Madagasikara)",
39083         "mg",
39084         "261"
39085       ],
39086       [
39087         "Malawi",
39088         "mw",
39089         "265"
39090       ],
39091       [
39092         "Malaysia",
39093         "my",
39094         "60"
39095       ],
39096       [
39097         "Maldives",
39098         "mv",
39099         "960"
39100       ],
39101       [
39102         "Mali",
39103         "ml",
39104         "223"
39105       ],
39106       [
39107         "Malta",
39108         "mt",
39109         "356"
39110       ],
39111       [
39112         "Marshall Islands",
39113         "mh",
39114         "692"
39115       ],
39116       [
39117         "Martinique",
39118         "mq",
39119         "596"
39120       ],
39121       [
39122         "Mauritania (‫موريتانيا‬‎)",
39123         "mr",
39124         "222"
39125       ],
39126       [
39127         "Mauritius (Moris)",
39128         "mu",
39129         "230"
39130       ],
39131       [
39132         "Mayotte",
39133         "yt",
39134         "262",
39135         1
39136       ],
39137       [
39138         "Mexico (México)",
39139         "mx",
39140         "52"
39141       ],
39142       [
39143         "Micronesia",
39144         "fm",
39145         "691"
39146       ],
39147       [
39148         "Moldova (Republica Moldova)",
39149         "md",
39150         "373"
39151       ],
39152       [
39153         "Monaco",
39154         "mc",
39155         "377"
39156       ],
39157       [
39158         "Mongolia (Монгол)",
39159         "mn",
39160         "976"
39161       ],
39162       [
39163         "Montenegro (Crna Gora)",
39164         "me",
39165         "382"
39166       ],
39167       [
39168         "Montserrat",
39169         "ms",
39170         "1664"
39171       ],
39172       [
39173         "Morocco (‫المغرب‬‎)",
39174         "ma",
39175         "212",
39176         0
39177       ],
39178       [
39179         "Mozambique (Moçambique)",
39180         "mz",
39181         "258"
39182       ],
39183       [
39184         "Myanmar (Burma) (မြန်မာ)",
39185         "mm",
39186         "95"
39187       ],
39188       [
39189         "Namibia (Namibië)",
39190         "na",
39191         "264"
39192       ],
39193       [
39194         "Nauru",
39195         "nr",
39196         "674"
39197       ],
39198       [
39199         "Nepal (नेपाल)",
39200         "np",
39201         "977"
39202       ],
39203       [
39204         "Netherlands (Nederland)",
39205         "nl",
39206         "31"
39207       ],
39208       [
39209         "New Caledonia (Nouvelle-Calédonie)",
39210         "nc",
39211         "687"
39212       ],
39213       [
39214         "New Zealand",
39215         "nz",
39216         "64"
39217       ],
39218       [
39219         "Nicaragua",
39220         "ni",
39221         "505"
39222       ],
39223       [
39224         "Niger (Nijar)",
39225         "ne",
39226         "227"
39227       ],
39228       [
39229         "Nigeria",
39230         "ng",
39231         "234"
39232       ],
39233       [
39234         "Niue",
39235         "nu",
39236         "683"
39237       ],
39238       [
39239         "Norfolk Island",
39240         "nf",
39241         "672"
39242       ],
39243       [
39244         "North Korea (조선 민주주의 인민 공화국)",
39245         "kp",
39246         "850"
39247       ],
39248       [
39249         "Northern Mariana Islands",
39250         "mp",
39251         "1670"
39252       ],
39253       [
39254         "Norway (Norge)",
39255         "no",
39256         "47",
39257         0
39258       ],
39259       [
39260         "Oman (‫عُمان‬‎)",
39261         "om",
39262         "968"
39263       ],
39264       [
39265         "Pakistan (‫پاکستان‬‎)",
39266         "pk",
39267         "92"
39268       ],
39269       [
39270         "Palau",
39271         "pw",
39272         "680"
39273       ],
39274       [
39275         "Palestine (‫فلسطين‬‎)",
39276         "ps",
39277         "970"
39278       ],
39279       [
39280         "Panama (Panamá)",
39281         "pa",
39282         "507"
39283       ],
39284       [
39285         "Papua New Guinea",
39286         "pg",
39287         "675"
39288       ],
39289       [
39290         "Paraguay",
39291         "py",
39292         "595"
39293       ],
39294       [
39295         "Peru (Perú)",
39296         "pe",
39297         "51"
39298       ],
39299       [
39300         "Philippines",
39301         "ph",
39302         "63"
39303       ],
39304       [
39305         "Poland (Polska)",
39306         "pl",
39307         "48"
39308       ],
39309       [
39310         "Portugal",
39311         "pt",
39312         "351"
39313       ],
39314       [
39315         "Puerto Rico",
39316         "pr",
39317         "1",
39318         3,
39319         ["787", "939"]
39320       ],
39321       [
39322         "Qatar (‫قطر‬‎)",
39323         "qa",
39324         "974"
39325       ],
39326       [
39327         "Réunion (La Réunion)",
39328         "re",
39329         "262",
39330         0
39331       ],
39332       [
39333         "Romania (România)",
39334         "ro",
39335         "40"
39336       ],
39337       [
39338         "Russia (Россия)",
39339         "ru",
39340         "7",
39341         0
39342       ],
39343       [
39344         "Rwanda",
39345         "rw",
39346         "250"
39347       ],
39348       [
39349         "Saint Barthélemy",
39350         "bl",
39351         "590",
39352         1
39353       ],
39354       [
39355         "Saint Helena",
39356         "sh",
39357         "290"
39358       ],
39359       [
39360         "Saint Kitts and Nevis",
39361         "kn",
39362         "1869"
39363       ],
39364       [
39365         "Saint Lucia",
39366         "lc",
39367         "1758"
39368       ],
39369       [
39370         "Saint Martin (Saint-Martin (partie française))",
39371         "mf",
39372         "590",
39373         2
39374       ],
39375       [
39376         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39377         "pm",
39378         "508"
39379       ],
39380       [
39381         "Saint Vincent and the Grenadines",
39382         "vc",
39383         "1784"
39384       ],
39385       [
39386         "Samoa",
39387         "ws",
39388         "685"
39389       ],
39390       [
39391         "San Marino",
39392         "sm",
39393         "378"
39394       ],
39395       [
39396         "São Tomé and Príncipe (São Tomé e Príncipe)",
39397         "st",
39398         "239"
39399       ],
39400       [
39401         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39402         "sa",
39403         "966"
39404       ],
39405       [
39406         "Senegal (Sénégal)",
39407         "sn",
39408         "221"
39409       ],
39410       [
39411         "Serbia (Србија)",
39412         "rs",
39413         "381"
39414       ],
39415       [
39416         "Seychelles",
39417         "sc",
39418         "248"
39419       ],
39420       [
39421         "Sierra Leone",
39422         "sl",
39423         "232"
39424       ],
39425       [
39426         "Singapore",
39427         "sg",
39428         "65"
39429       ],
39430       [
39431         "Sint Maarten",
39432         "sx",
39433         "1721"
39434       ],
39435       [
39436         "Slovakia (Slovensko)",
39437         "sk",
39438         "421"
39439       ],
39440       [
39441         "Slovenia (Slovenija)",
39442         "si",
39443         "386"
39444       ],
39445       [
39446         "Solomon Islands",
39447         "sb",
39448         "677"
39449       ],
39450       [
39451         "Somalia (Soomaaliya)",
39452         "so",
39453         "252"
39454       ],
39455       [
39456         "South Africa",
39457         "za",
39458         "27"
39459       ],
39460       [
39461         "South Korea (대한민국)",
39462         "kr",
39463         "82"
39464       ],
39465       [
39466         "South Sudan (‫جنوب السودان‬‎)",
39467         "ss",
39468         "211"
39469       ],
39470       [
39471         "Spain (España)",
39472         "es",
39473         "34"
39474       ],
39475       [
39476         "Sri Lanka (ශ්‍රී ලංකාව)",
39477         "lk",
39478         "94"
39479       ],
39480       [
39481         "Sudan (‫السودان‬‎)",
39482         "sd",
39483         "249"
39484       ],
39485       [
39486         "Suriname",
39487         "sr",
39488         "597"
39489       ],
39490       [
39491         "Svalbard and Jan Mayen",
39492         "sj",
39493         "47",
39494         1
39495       ],
39496       [
39497         "Swaziland",
39498         "sz",
39499         "268"
39500       ],
39501       [
39502         "Sweden (Sverige)",
39503         "se",
39504         "46"
39505       ],
39506       [
39507         "Switzerland (Schweiz)",
39508         "ch",
39509         "41"
39510       ],
39511       [
39512         "Syria (‫سوريا‬‎)",
39513         "sy",
39514         "963"
39515       ],
39516       [
39517         "Taiwan (台灣)",
39518         "tw",
39519         "886"
39520       ],
39521       [
39522         "Tajikistan",
39523         "tj",
39524         "992"
39525       ],
39526       [
39527         "Tanzania",
39528         "tz",
39529         "255"
39530       ],
39531       [
39532         "Thailand (ไทย)",
39533         "th",
39534         "66"
39535       ],
39536       [
39537         "Timor-Leste",
39538         "tl",
39539         "670"
39540       ],
39541       [
39542         "Togo",
39543         "tg",
39544         "228"
39545       ],
39546       [
39547         "Tokelau",
39548         "tk",
39549         "690"
39550       ],
39551       [
39552         "Tonga",
39553         "to",
39554         "676"
39555       ],
39556       [
39557         "Trinidad and Tobago",
39558         "tt",
39559         "1868"
39560       ],
39561       [
39562         "Tunisia (‫تونس‬‎)",
39563         "tn",
39564         "216"
39565       ],
39566       [
39567         "Turkey (Türkiye)",
39568         "tr",
39569         "90"
39570       ],
39571       [
39572         "Turkmenistan",
39573         "tm",
39574         "993"
39575       ],
39576       [
39577         "Turks and Caicos Islands",
39578         "tc",
39579         "1649"
39580       ],
39581       [
39582         "Tuvalu",
39583         "tv",
39584         "688"
39585       ],
39586       [
39587         "U.S. Virgin Islands",
39588         "vi",
39589         "1340"
39590       ],
39591       [
39592         "Uganda",
39593         "ug",
39594         "256"
39595       ],
39596       [
39597         "Ukraine (Україна)",
39598         "ua",
39599         "380"
39600       ],
39601       [
39602         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39603         "ae",
39604         "971"
39605       ],
39606       [
39607         "United Kingdom",
39608         "gb",
39609         "44",
39610         0
39611       ],
39612       [
39613         "United States",
39614         "us",
39615         "1",
39616         0
39617       ],
39618       [
39619         "Uruguay",
39620         "uy",
39621         "598"
39622       ],
39623       [
39624         "Uzbekistan (Oʻzbekiston)",
39625         "uz",
39626         "998"
39627       ],
39628       [
39629         "Vanuatu",
39630         "vu",
39631         "678"
39632       ],
39633       [
39634         "Vatican City (Città del Vaticano)",
39635         "va",
39636         "39",
39637         1
39638       ],
39639       [
39640         "Venezuela",
39641         "ve",
39642         "58"
39643       ],
39644       [
39645         "Vietnam (Việt Nam)",
39646         "vn",
39647         "84"
39648       ],
39649       [
39650         "Wallis and Futuna (Wallis-et-Futuna)",
39651         "wf",
39652         "681"
39653       ],
39654       [
39655         "Western Sahara (‫الصحراء الغربية‬‎)",
39656         "eh",
39657         "212",
39658         1
39659       ],
39660       [
39661         "Yemen (‫اليمن‬‎)",
39662         "ye",
39663         "967"
39664       ],
39665       [
39666         "Zambia",
39667         "zm",
39668         "260"
39669       ],
39670       [
39671         "Zimbabwe",
39672         "zw",
39673         "263"
39674       ],
39675       [
39676         "Åland Islands",
39677         "ax",
39678         "358",
39679         1
39680       ]
39681   ];
39682   
39683   return d;
39684 }/**
39685 *    This script refer to:
39686 *    Title: International Telephone Input
39687 *    Author: Jack O'Connor
39688 *    Code version:  v12.1.12
39689 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39690 **/
39691
39692 /**
39693  * @class Roo.bootstrap.PhoneInput
39694  * @extends Roo.bootstrap.TriggerField
39695  * An input with International dial-code selection
39696  
39697  * @cfg {String} defaultDialCode default '+852'
39698  * @cfg {Array} preferedCountries default []
39699   
39700  * @constructor
39701  * Create a new PhoneInput.
39702  * @param {Object} config Configuration options
39703  */
39704
39705 Roo.bootstrap.PhoneInput = function(config) {
39706     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39707 };
39708
39709 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39710         
39711         listWidth: undefined,
39712         
39713         selectedClass: 'active',
39714         
39715         invalidClass : "has-warning",
39716         
39717         validClass: 'has-success',
39718         
39719         allowed: '0123456789',
39720         
39721         /**
39722          * @cfg {String} defaultDialCode The default dial code when initializing the input
39723          */
39724         defaultDialCode: '+852',
39725         
39726         /**
39727          * @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
39728          */
39729         preferedCountries: false,
39730         
39731         getAutoCreate : function()
39732         {
39733             var data = Roo.bootstrap.PhoneInputData();
39734             var align = this.labelAlign || this.parentLabelAlign();
39735             var id = Roo.id();
39736             
39737             this.allCountries = [];
39738             this.dialCodeMapping = [];
39739             
39740             for (var i = 0; i < data.length; i++) {
39741               var c = data[i];
39742               this.allCountries[i] = {
39743                 name: c[0],
39744                 iso2: c[1],
39745                 dialCode: c[2],
39746                 priority: c[3] || 0,
39747                 areaCodes: c[4] || null
39748               };
39749               this.dialCodeMapping[c[2]] = {
39750                   name: c[0],
39751                   iso2: c[1],
39752                   priority: c[3] || 0,
39753                   areaCodes: c[4] || null
39754               };
39755             }
39756             
39757             var cfg = {
39758                 cls: 'form-group',
39759                 cn: []
39760             };
39761             
39762             var input =  {
39763                 tag: 'input',
39764                 id : id,
39765                 cls : 'form-control tel-input',
39766                 autocomplete: 'new-password'
39767             };
39768             
39769             var hiddenInput = {
39770                 tag: 'input',
39771                 type: 'hidden',
39772                 cls: 'hidden-tel-input'
39773             };
39774             
39775             if (this.name) {
39776                 hiddenInput.name = this.name;
39777             }
39778             
39779             if (this.disabled) {
39780                 input.disabled = true;
39781             }
39782             
39783             var flag_container = {
39784                 tag: 'div',
39785                 cls: 'flag-box',
39786                 cn: [
39787                     {
39788                         tag: 'div',
39789                         cls: 'flag'
39790                     },
39791                     {
39792                         tag: 'div',
39793                         cls: 'caret'
39794                     }
39795                 ]
39796             };
39797             
39798             var box = {
39799                 tag: 'div',
39800                 cls: this.hasFeedback ? 'has-feedback' : '',
39801                 cn: [
39802                     hiddenInput,
39803                     input,
39804                     {
39805                         tag: 'input',
39806                         cls: 'dial-code-holder',
39807                         disabled: true
39808                     }
39809                 ]
39810             };
39811             
39812             var container = {
39813                 cls: 'roo-select2-container input-group',
39814                 cn: [
39815                     flag_container,
39816                     box
39817                 ]
39818             };
39819             
39820             if (this.fieldLabel.length) {
39821                 var indicator = {
39822                     tag: 'i',
39823                     tooltip: 'This field is required'
39824                 };
39825                 
39826                 var label = {
39827                     tag: 'label',
39828                     'for':  id,
39829                     cls: 'control-label',
39830                     cn: []
39831                 };
39832                 
39833                 var label_text = {
39834                     tag: 'span',
39835                     html: this.fieldLabel
39836                 };
39837                 
39838                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39839                 label.cn = [
39840                     indicator,
39841                     label_text
39842                 ];
39843                 
39844                 if(this.indicatorpos == 'right') {
39845                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39846                     label.cn = [
39847                         label_text,
39848                         indicator
39849                     ];
39850                 }
39851                 
39852                 if(align == 'left') {
39853                     container = {
39854                         tag: 'div',
39855                         cn: [
39856                             container
39857                         ]
39858                     };
39859                     
39860                     if(this.labelWidth > 12){
39861                         label.style = "width: " + this.labelWidth + 'px';
39862                     }
39863                     if(this.labelWidth < 13 && this.labelmd == 0){
39864                         this.labelmd = this.labelWidth;
39865                     }
39866                     if(this.labellg > 0){
39867                         label.cls += ' col-lg-' + this.labellg;
39868                         input.cls += ' col-lg-' + (12 - this.labellg);
39869                     }
39870                     if(this.labelmd > 0){
39871                         label.cls += ' col-md-' + this.labelmd;
39872                         container.cls += ' col-md-' + (12 - this.labelmd);
39873                     }
39874                     if(this.labelsm > 0){
39875                         label.cls += ' col-sm-' + this.labelsm;
39876                         container.cls += ' col-sm-' + (12 - this.labelsm);
39877                     }
39878                     if(this.labelxs > 0){
39879                         label.cls += ' col-xs-' + this.labelxs;
39880                         container.cls += ' col-xs-' + (12 - this.labelxs);
39881                     }
39882                 }
39883             }
39884             
39885             cfg.cn = [
39886                 label,
39887                 container
39888             ];
39889             
39890             var settings = this;
39891             
39892             ['xs','sm','md','lg'].map(function(size){
39893                 if (settings[size]) {
39894                     cfg.cls += ' col-' + size + '-' + settings[size];
39895                 }
39896             });
39897             
39898             this.store = new Roo.data.Store({
39899                 proxy : new Roo.data.MemoryProxy({}),
39900                 reader : new Roo.data.JsonReader({
39901                     fields : [
39902                         {
39903                             'name' : 'name',
39904                             'type' : 'string'
39905                         },
39906                         {
39907                             'name' : 'iso2',
39908                             'type' : 'string'
39909                         },
39910                         {
39911                             'name' : 'dialCode',
39912                             'type' : 'string'
39913                         },
39914                         {
39915                             'name' : 'priority',
39916                             'type' : 'string'
39917                         },
39918                         {
39919                             'name' : 'areaCodes',
39920                             'type' : 'string'
39921                         }
39922                     ]
39923                 })
39924             });
39925             
39926             if(!this.preferedCountries) {
39927                 this.preferedCountries = [
39928                     'hk',
39929                     'gb',
39930                     'us'
39931                 ];
39932             }
39933             
39934             var p = this.preferedCountries.reverse();
39935             
39936             if(p) {
39937                 for (var i = 0; i < p.length; i++) {
39938                     for (var j = 0; j < this.allCountries.length; j++) {
39939                         if(this.allCountries[j].iso2 == p[i]) {
39940                             var t = this.allCountries[j];
39941                             this.allCountries.splice(j,1);
39942                             this.allCountries.unshift(t);
39943                         }
39944                     } 
39945                 }
39946             }
39947             
39948             this.store.proxy.data = {
39949                 success: true,
39950                 data: this.allCountries
39951             };
39952             
39953             return cfg;
39954         },
39955         
39956         initEvents : function()
39957         {
39958             this.createList();
39959             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39960             
39961             this.indicator = this.indicatorEl();
39962             this.flag = this.flagEl();
39963             this.dialCodeHolder = this.dialCodeHolderEl();
39964             
39965             this.trigger = this.el.select('div.flag-box',true).first();
39966             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39967             
39968             var _this = this;
39969             
39970             (function(){
39971                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39972                 _this.list.setWidth(lw);
39973             }).defer(100);
39974             
39975             this.list.on('mouseover', this.onViewOver, this);
39976             this.list.on('mousemove', this.onViewMove, this);
39977             this.inputEl().on("keyup", this.onKeyUp, this);
39978             
39979             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39980
39981             this.view = new Roo.View(this.list, this.tpl, {
39982                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39983             });
39984             
39985             this.view.on('click', this.onViewClick, this);
39986             this.setValue(this.defaultDialCode);
39987         },
39988         
39989         onTriggerClick : function(e)
39990         {
39991             Roo.log('trigger click');
39992             if(this.disabled){
39993                 return;
39994             }
39995             
39996             if(this.isExpanded()){
39997                 this.collapse();
39998                 this.hasFocus = false;
39999             }else {
40000                 this.store.load({});
40001                 this.hasFocus = true;
40002                 this.expand();
40003             }
40004         },
40005         
40006         isExpanded : function()
40007         {
40008             return this.list.isVisible();
40009         },
40010         
40011         collapse : function()
40012         {
40013             if(!this.isExpanded()){
40014                 return;
40015             }
40016             this.list.hide();
40017             Roo.get(document).un('mousedown', this.collapseIf, this);
40018             Roo.get(document).un('mousewheel', this.collapseIf, this);
40019             this.fireEvent('collapse', this);
40020             this.validate();
40021         },
40022         
40023         expand : function()
40024         {
40025             Roo.log('expand');
40026
40027             if(this.isExpanded() || !this.hasFocus){
40028                 return;
40029             }
40030             
40031             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40032             this.list.setWidth(lw);
40033             
40034             this.list.show();
40035             this.restrictHeight();
40036             
40037             Roo.get(document).on('mousedown', this.collapseIf, this);
40038             Roo.get(document).on('mousewheel', this.collapseIf, this);
40039             
40040             this.fireEvent('expand', this);
40041         },
40042         
40043         restrictHeight : function()
40044         {
40045             this.list.alignTo(this.inputEl(), this.listAlign);
40046             this.list.alignTo(this.inputEl(), this.listAlign);
40047         },
40048         
40049         onViewOver : function(e, t)
40050         {
40051             if(this.inKeyMode){
40052                 return;
40053             }
40054             var item = this.view.findItemFromChild(t);
40055             
40056             if(item){
40057                 var index = this.view.indexOf(item);
40058                 this.select(index, false);
40059             }
40060         },
40061
40062         // private
40063         onViewClick : function(view, doFocus, el, e)
40064         {
40065             var index = this.view.getSelectedIndexes()[0];
40066             
40067             var r = this.store.getAt(index);
40068             
40069             if(r){
40070                 this.onSelect(r, index);
40071             }
40072             if(doFocus !== false && !this.blockFocus){
40073                 this.inputEl().focus();
40074             }
40075         },
40076         
40077         onViewMove : function(e, t)
40078         {
40079             this.inKeyMode = false;
40080         },
40081         
40082         select : function(index, scrollIntoView)
40083         {
40084             this.selectedIndex = index;
40085             this.view.select(index);
40086             if(scrollIntoView !== false){
40087                 var el = this.view.getNode(index);
40088                 if(el){
40089                     this.list.scrollChildIntoView(el, false);
40090                 }
40091             }
40092         },
40093         
40094         createList : function()
40095         {
40096             this.list = Roo.get(document.body).createChild({
40097                 tag: 'ul',
40098                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40099                 style: 'display:none'
40100             });
40101             
40102             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40103         },
40104         
40105         collapseIf : function(e)
40106         {
40107             var in_combo  = e.within(this.el);
40108             var in_list =  e.within(this.list);
40109             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40110             
40111             if (in_combo || in_list || is_list) {
40112                 return;
40113             }
40114             this.collapse();
40115         },
40116         
40117         onSelect : function(record, index)
40118         {
40119             if(this.fireEvent('beforeselect', this, record, index) !== false){
40120                 
40121                 this.setFlagClass(record.data.iso2);
40122                 this.setDialCode(record.data.dialCode);
40123                 this.hasFocus = false;
40124                 this.collapse();
40125                 this.fireEvent('select', this, record, index);
40126             }
40127         },
40128         
40129         flagEl : function()
40130         {
40131             var flag = this.el.select('div.flag',true).first();
40132             if(!flag){
40133                 return false;
40134             }
40135             return flag;
40136         },
40137         
40138         dialCodeHolderEl : function()
40139         {
40140             var d = this.el.select('input.dial-code-holder',true).first();
40141             if(!d){
40142                 return false;
40143             }
40144             return d;
40145         },
40146         
40147         setDialCode : function(v)
40148         {
40149             this.dialCodeHolder.dom.value = '+'+v;
40150         },
40151         
40152         setFlagClass : function(n)
40153         {
40154             this.flag.dom.className = 'flag '+n;
40155         },
40156         
40157         getValue : function()
40158         {
40159             var v = this.inputEl().getValue();
40160             if(this.dialCodeHolder) {
40161                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40162             }
40163             return v;
40164         },
40165         
40166         setValue : function(v)
40167         {
40168             var d = this.getDialCode(v);
40169             
40170             //invalid dial code
40171             if(v.length == 0 || !d || d.length == 0) {
40172                 if(this.rendered){
40173                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40174                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40175                 }
40176                 return;
40177             }
40178             
40179             //valid dial code
40180             this.setFlagClass(this.dialCodeMapping[d].iso2);
40181             this.setDialCode(d);
40182             this.inputEl().dom.value = v.replace('+'+d,'');
40183             this.hiddenEl().dom.value = this.getValue();
40184             
40185             this.validate();
40186         },
40187         
40188         getDialCode : function(v)
40189         {
40190             v = v ||  '';
40191             
40192             if (v.length == 0) {
40193                 return this.dialCodeHolder.dom.value;
40194             }
40195             
40196             var dialCode = "";
40197             if (v.charAt(0) != "+") {
40198                 return false;
40199             }
40200             var numericChars = "";
40201             for (var i = 1; i < v.length; i++) {
40202               var c = v.charAt(i);
40203               if (!isNaN(c)) {
40204                 numericChars += c;
40205                 if (this.dialCodeMapping[numericChars]) {
40206                   dialCode = v.substr(1, i);
40207                 }
40208                 if (numericChars.length == 4) {
40209                   break;
40210                 }
40211               }
40212             }
40213             return dialCode;
40214         },
40215         
40216         reset : function()
40217         {
40218             this.setValue(this.defaultDialCode);
40219             this.validate();
40220         },
40221         
40222         hiddenEl : function()
40223         {
40224             return this.el.select('input.hidden-tel-input',true).first();
40225         },
40226         
40227         onKeyUp : function(e){
40228             
40229             var k = e.getKey();
40230             var c = e.getCharCode();
40231             
40232             if(
40233                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40234                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40235             ){
40236                 e.stopEvent();
40237             }
40238             
40239             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40240             //     return;
40241             // }
40242             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40243                 e.stopEvent();
40244             }
40245             
40246             this.setValue(this.getValue());
40247         }
40248         
40249 });
40250 /**
40251  * @class Roo.bootstrap.MoneyField
40252  * @extends Roo.bootstrap.ComboBox
40253  * Bootstrap MoneyField class
40254  * 
40255  * @constructor
40256  * Create a new MoneyField.
40257  * @param {Object} config Configuration options
40258  */
40259
40260 Roo.bootstrap.MoneyField = function(config) {
40261     
40262     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40263     
40264 };
40265
40266 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40267     
40268     /**
40269      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40270      */
40271     allowDecimals : true,
40272     /**
40273      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40274      */
40275     decimalSeparator : ".",
40276     /**
40277      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40278      */
40279     decimalPrecision : 0,
40280     /**
40281      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40282      */
40283     allowNegative : true,
40284     /**
40285      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40286      */
40287     allowZero: true,
40288     /**
40289      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40290      */
40291     minValue : Number.NEGATIVE_INFINITY,
40292     /**
40293      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40294      */
40295     maxValue : Number.MAX_VALUE,
40296     /**
40297      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40298      */
40299     minText : "The minimum value for this field is {0}",
40300     /**
40301      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40302      */
40303     maxText : "The maximum value for this field is {0}",
40304     /**
40305      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40306      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40307      */
40308     nanText : "{0} is not a valid number",
40309     /**
40310      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40311      */
40312     castInt : true,
40313     /**
40314      * @cfg {String} defaults currency of the MoneyField
40315      * value should be in lkey
40316      */
40317     defaultCurrency : false,
40318     /**
40319      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40320      */
40321     thousandsDelimiter : false,
40322     
40323     
40324     inputlg : 9,
40325     inputmd : 9,
40326     inputsm : 9,
40327     inputxs : 6,
40328     
40329     store : false,
40330     
40331     getAutoCreate : function()
40332     {
40333         var align = this.labelAlign || this.parentLabelAlign();
40334         
40335         var id = Roo.id();
40336
40337         var cfg = {
40338             cls: 'form-group',
40339             cn: []
40340         };
40341
40342         var input =  {
40343             tag: 'input',
40344             id : id,
40345             cls : 'form-control roo-money-amount-input',
40346             autocomplete: 'new-password'
40347         };
40348         
40349         var hiddenInput = {
40350             tag: 'input',
40351             type: 'hidden',
40352             id: Roo.id(),
40353             cls: 'hidden-number-input'
40354         };
40355         
40356         if (this.name) {
40357             hiddenInput.name = this.name;
40358         }
40359
40360         if (this.disabled) {
40361             input.disabled = true;
40362         }
40363
40364         var clg = 12 - this.inputlg;
40365         var cmd = 12 - this.inputmd;
40366         var csm = 12 - this.inputsm;
40367         var cxs = 12 - this.inputxs;
40368         
40369         var container = {
40370             tag : 'div',
40371             cls : 'row roo-money-field',
40372             cn : [
40373                 {
40374                     tag : 'div',
40375                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40376                     cn : [
40377                         {
40378                             tag : 'div',
40379                             cls: 'roo-select2-container input-group',
40380                             cn: [
40381                                 {
40382                                     tag : 'input',
40383                                     cls : 'form-control roo-money-currency-input',
40384                                     autocomplete: 'new-password',
40385                                     readOnly : 1,
40386                                     name : this.currencyName
40387                                 },
40388                                 {
40389                                     tag :'span',
40390                                     cls : 'input-group-addon',
40391                                     cn : [
40392                                         {
40393                                             tag: 'span',
40394                                             cls: 'caret'
40395                                         }
40396                                     ]
40397                                 }
40398                             ]
40399                         }
40400                     ]
40401                 },
40402                 {
40403                     tag : 'div',
40404                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40405                     cn : [
40406                         {
40407                             tag: 'div',
40408                             cls: this.hasFeedback ? 'has-feedback' : '',
40409                             cn: [
40410                                 input
40411                             ]
40412                         }
40413                     ]
40414                 }
40415             ]
40416             
40417         };
40418         
40419         if (this.fieldLabel.length) {
40420             var indicator = {
40421                 tag: 'i',
40422                 tooltip: 'This field is required'
40423             };
40424
40425             var label = {
40426                 tag: 'label',
40427                 'for':  id,
40428                 cls: 'control-label',
40429                 cn: []
40430             };
40431
40432             var label_text = {
40433                 tag: 'span',
40434                 html: this.fieldLabel
40435             };
40436
40437             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40438             label.cn = [
40439                 indicator,
40440                 label_text
40441             ];
40442
40443             if(this.indicatorpos == 'right') {
40444                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40445                 label.cn = [
40446                     label_text,
40447                     indicator
40448                 ];
40449             }
40450
40451             if(align == 'left') {
40452                 container = {
40453                     tag: 'div',
40454                     cn: [
40455                         container
40456                     ]
40457                 };
40458
40459                 if(this.labelWidth > 12){
40460                     label.style = "width: " + this.labelWidth + 'px';
40461                 }
40462                 if(this.labelWidth < 13 && this.labelmd == 0){
40463                     this.labelmd = this.labelWidth;
40464                 }
40465                 if(this.labellg > 0){
40466                     label.cls += ' col-lg-' + this.labellg;
40467                     input.cls += ' col-lg-' + (12 - this.labellg);
40468                 }
40469                 if(this.labelmd > 0){
40470                     label.cls += ' col-md-' + this.labelmd;
40471                     container.cls += ' col-md-' + (12 - this.labelmd);
40472                 }
40473                 if(this.labelsm > 0){
40474                     label.cls += ' col-sm-' + this.labelsm;
40475                     container.cls += ' col-sm-' + (12 - this.labelsm);
40476                 }
40477                 if(this.labelxs > 0){
40478                     label.cls += ' col-xs-' + this.labelxs;
40479                     container.cls += ' col-xs-' + (12 - this.labelxs);
40480                 }
40481             }
40482         }
40483
40484         cfg.cn = [
40485             label,
40486             container,
40487             hiddenInput
40488         ];
40489         
40490         var settings = this;
40491
40492         ['xs','sm','md','lg'].map(function(size){
40493             if (settings[size]) {
40494                 cfg.cls += ' col-' + size + '-' + settings[size];
40495             }
40496         });
40497         
40498         return cfg;
40499     },
40500     
40501     initEvents : function()
40502     {
40503         this.indicator = this.indicatorEl();
40504         
40505         this.initCurrencyEvent();
40506         
40507         this.initNumberEvent();
40508     },
40509     
40510     initCurrencyEvent : function()
40511     {
40512         if (!this.store) {
40513             throw "can not find store for combo";
40514         }
40515         
40516         this.store = Roo.factory(this.store, Roo.data);
40517         this.store.parent = this;
40518         
40519         this.createList();
40520         
40521         this.triggerEl = this.el.select('.input-group-addon', true).first();
40522         
40523         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40524         
40525         var _this = this;
40526         
40527         (function(){
40528             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40529             _this.list.setWidth(lw);
40530         }).defer(100);
40531         
40532         this.list.on('mouseover', this.onViewOver, this);
40533         this.list.on('mousemove', this.onViewMove, this);
40534         this.list.on('scroll', this.onViewScroll, this);
40535         
40536         if(!this.tpl){
40537             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40538         }
40539         
40540         this.view = new Roo.View(this.list, this.tpl, {
40541             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40542         });
40543         
40544         this.view.on('click', this.onViewClick, this);
40545         
40546         this.store.on('beforeload', this.onBeforeLoad, this);
40547         this.store.on('load', this.onLoad, this);
40548         this.store.on('loadexception', this.onLoadException, this);
40549         
40550         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40551             "up" : function(e){
40552                 this.inKeyMode = true;
40553                 this.selectPrev();
40554             },
40555
40556             "down" : function(e){
40557                 if(!this.isExpanded()){
40558                     this.onTriggerClick();
40559                 }else{
40560                     this.inKeyMode = true;
40561                     this.selectNext();
40562                 }
40563             },
40564
40565             "enter" : function(e){
40566                 this.collapse();
40567                 
40568                 if(this.fireEvent("specialkey", this, e)){
40569                     this.onViewClick(false);
40570                 }
40571                 
40572                 return true;
40573             },
40574
40575             "esc" : function(e){
40576                 this.collapse();
40577             },
40578
40579             "tab" : function(e){
40580                 this.collapse();
40581                 
40582                 if(this.fireEvent("specialkey", this, e)){
40583                     this.onViewClick(false);
40584                 }
40585                 
40586                 return true;
40587             },
40588
40589             scope : this,
40590
40591             doRelay : function(foo, bar, hname){
40592                 if(hname == 'down' || this.scope.isExpanded()){
40593                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40594                 }
40595                 return true;
40596             },
40597
40598             forceKeyDown: true
40599         });
40600         
40601         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40602         
40603     },
40604     
40605     initNumberEvent : function(e)
40606     {
40607         this.inputEl().on("keydown" , this.fireKey,  this);
40608         this.inputEl().on("focus", this.onFocus,  this);
40609         this.inputEl().on("blur", this.onBlur,  this);
40610         
40611         this.inputEl().relayEvent('keyup', this);
40612         
40613         if(this.indicator){
40614             this.indicator.addClass('invisible');
40615         }
40616  
40617         this.originalValue = this.getValue();
40618         
40619         if(this.validationEvent == 'keyup'){
40620             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40621             this.inputEl().on('keyup', this.filterValidation, this);
40622         }
40623         else if(this.validationEvent !== false){
40624             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40625         }
40626         
40627         if(this.selectOnFocus){
40628             this.on("focus", this.preFocus, this);
40629             
40630         }
40631         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40632             this.inputEl().on("keypress", this.filterKeys, this);
40633         } else {
40634             this.inputEl().relayEvent('keypress', this);
40635         }
40636         
40637         var allowed = "0123456789";
40638         
40639         if(this.allowDecimals){
40640             allowed += this.decimalSeparator;
40641         }
40642         
40643         if(this.allowNegative){
40644             allowed += "-";
40645         }
40646         
40647         if(this.thousandsDelimiter) {
40648             allowed += ",";
40649         }
40650         
40651         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40652         
40653         var keyPress = function(e){
40654             
40655             var k = e.getKey();
40656             
40657             var c = e.getCharCode();
40658             
40659             if(
40660                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40661                     allowed.indexOf(String.fromCharCode(c)) === -1
40662             ){
40663                 e.stopEvent();
40664                 return;
40665             }
40666             
40667             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40668                 return;
40669             }
40670             
40671             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40672                 e.stopEvent();
40673             }
40674         };
40675         
40676         this.inputEl().on("keypress", keyPress, this);
40677         
40678     },
40679     
40680     onTriggerClick : function(e)
40681     {   
40682         if(this.disabled){
40683             return;
40684         }
40685         
40686         this.page = 0;
40687         this.loadNext = false;
40688         
40689         if(this.isExpanded()){
40690             this.collapse();
40691             return;
40692         }
40693         
40694         this.hasFocus = true;
40695         
40696         if(this.triggerAction == 'all') {
40697             this.doQuery(this.allQuery, true);
40698             return;
40699         }
40700         
40701         this.doQuery(this.getRawValue());
40702     },
40703     
40704     getCurrency : function()
40705     {   
40706         var v = this.currencyEl().getValue();
40707         
40708         return v;
40709     },
40710     
40711     restrictHeight : function()
40712     {
40713         this.list.alignTo(this.currencyEl(), this.listAlign);
40714         this.list.alignTo(this.currencyEl(), this.listAlign);
40715     },
40716     
40717     onViewClick : function(view, doFocus, el, e)
40718     {
40719         var index = this.view.getSelectedIndexes()[0];
40720         
40721         var r = this.store.getAt(index);
40722         
40723         if(r){
40724             this.onSelect(r, index);
40725         }
40726     },
40727     
40728     onSelect : function(record, index){
40729         
40730         if(this.fireEvent('beforeselect', this, record, index) !== false){
40731         
40732             this.setFromCurrencyData(index > -1 ? record.data : false);
40733             
40734             this.collapse();
40735             
40736             this.fireEvent('select', this, record, index);
40737         }
40738     },
40739     
40740     setFromCurrencyData : function(o)
40741     {
40742         var currency = '';
40743         
40744         this.lastCurrency = o;
40745         
40746         if (this.currencyField) {
40747             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40748         } else {
40749             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40750         }
40751         
40752         this.lastSelectionText = currency;
40753         
40754         //setting default currency
40755         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40756             this.setCurrency(this.defaultCurrency);
40757             return;
40758         }
40759         
40760         this.setCurrency(currency);
40761     },
40762     
40763     setFromData : function(o)
40764     {
40765         var c = {};
40766         
40767         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40768         
40769         this.setFromCurrencyData(c);
40770         
40771         var value = '';
40772         
40773         if (this.name) {
40774             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40775         } else {
40776             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40777         }
40778         
40779         this.setValue(value);
40780         
40781     },
40782     
40783     setCurrency : function(v)
40784     {   
40785         this.currencyValue = v;
40786         
40787         if(this.rendered){
40788             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40789             this.validate();
40790         }
40791     },
40792     
40793     setValue : function(v)
40794     {
40795         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40796         
40797         this.value = v;
40798         
40799         if(this.rendered){
40800             
40801             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40802             
40803             this.inputEl().dom.value = (v == '') ? '' :
40804                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40805             
40806             if(!this.allowZero && v === '0') {
40807                 this.hiddenEl().dom.value = '';
40808                 this.inputEl().dom.value = '';
40809             }
40810             
40811             this.validate();
40812         }
40813     },
40814     
40815     getRawValue : function()
40816     {
40817         var v = this.inputEl().getValue();
40818         
40819         return v;
40820     },
40821     
40822     getValue : function()
40823     {
40824         return this.fixPrecision(this.parseValue(this.getRawValue()));
40825     },
40826     
40827     parseValue : function(value)
40828     {
40829         if(this.thousandsDelimiter) {
40830             value += "";
40831             r = new RegExp(",", "g");
40832             value = value.replace(r, "");
40833         }
40834         
40835         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40836         return isNaN(value) ? '' : value;
40837         
40838     },
40839     
40840     fixPrecision : function(value)
40841     {
40842         if(this.thousandsDelimiter) {
40843             value += "";
40844             r = new RegExp(",", "g");
40845             value = value.replace(r, "");
40846         }
40847         
40848         var nan = isNaN(value);
40849         
40850         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40851             return nan ? '' : value;
40852         }
40853         return parseFloat(value).toFixed(this.decimalPrecision);
40854     },
40855     
40856     decimalPrecisionFcn : function(v)
40857     {
40858         return Math.floor(v);
40859     },
40860     
40861     validateValue : function(value)
40862     {
40863         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40864             return false;
40865         }
40866         
40867         var num = this.parseValue(value);
40868         
40869         if(isNaN(num)){
40870             this.markInvalid(String.format(this.nanText, value));
40871             return false;
40872         }
40873         
40874         if(num < this.minValue){
40875             this.markInvalid(String.format(this.minText, this.minValue));
40876             return false;
40877         }
40878         
40879         if(num > this.maxValue){
40880             this.markInvalid(String.format(this.maxText, this.maxValue));
40881             return false;
40882         }
40883         
40884         return true;
40885     },
40886     
40887     validate : function()
40888     {
40889         if(this.disabled || this.allowBlank){
40890             this.markValid();
40891             return true;
40892         }
40893         
40894         var currency = this.getCurrency();
40895         
40896         if(this.validateValue(this.getRawValue()) && currency.length){
40897             this.markValid();
40898             return true;
40899         }
40900         
40901         this.markInvalid();
40902         return false;
40903     },
40904     
40905     getName: function()
40906     {
40907         return this.name;
40908     },
40909     
40910     beforeBlur : function()
40911     {
40912         if(!this.castInt){
40913             return;
40914         }
40915         
40916         var v = this.parseValue(this.getRawValue());
40917         
40918         if(v || v == 0){
40919             this.setValue(v);
40920         }
40921     },
40922     
40923     onBlur : function()
40924     {
40925         this.beforeBlur();
40926         
40927         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40928             //this.el.removeClass(this.focusClass);
40929         }
40930         
40931         this.hasFocus = false;
40932         
40933         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40934             this.validate();
40935         }
40936         
40937         var v = this.getValue();
40938         
40939         if(String(v) !== String(this.startValue)){
40940             this.fireEvent('change', this, v, this.startValue);
40941         }
40942         
40943         this.fireEvent("blur", this);
40944     },
40945     
40946     inputEl : function()
40947     {
40948         return this.el.select('.roo-money-amount-input', true).first();
40949     },
40950     
40951     currencyEl : function()
40952     {
40953         return this.el.select('.roo-money-currency-input', true).first();
40954     },
40955     
40956     hiddenEl : function()
40957     {
40958         return this.el.select('input.hidden-number-input',true).first();
40959     }
40960     
40961 });