4db33e6f0034cd195a66c5a09871531a66dc0d28
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     hide: function() {
921        
922      
923         this.el.hide();   
924     },
925     show: function() {
926        
927         this.el.show();   
928     },
929     setWeight : function(str)
930     {
931         this.el.removeClass(this.weightClass);
932         this.el.addClass('btn-' + str);        
933     }
934     
935     
936 });
937
938  /*
939  * - LGPL
940  *
941  * column
942  * 
943  */
944
945 /**
946  * @class Roo.bootstrap.Column
947  * @extends Roo.bootstrap.Component
948  * Bootstrap Column class
949  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
950  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
951  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
952  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
953  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
954  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
955  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
956  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
957  *
958  * 
959  * @cfg {Boolean} hidden (true|false) hide the element
960  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
961  * @cfg {String} fa (ban|check|...) font awesome icon
962  * @cfg {Number} fasize (1|2|....) font awsome size
963
964  * @cfg {String} icon (info-sign|check|...) glyphicon name
965
966  * @cfg {String} html content of column.
967  * 
968  * @constructor
969  * Create a new Column
970  * @param {Object} config The config object
971  */
972
973 Roo.bootstrap.Column = function(config){
974     Roo.bootstrap.Column.superclass.constructor.call(this, config);
975 };
976
977 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
978     
979     xs: false,
980     sm: false,
981     md: false,
982     lg: false,
983     xsoff: false,
984     smoff: false,
985     mdoff: false,
986     lgoff: false,
987     html: '',
988     offset: 0,
989     alert: false,
990     fa: false,
991     icon : false,
992     hidden : false,
993     fasize : 1,
994     
995     getAutoCreate : function(){
996         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
997         
998         cfg = {
999             tag: 'div',
1000             cls: 'column'
1001         };
1002         
1003         var settings=this;
1004         ['xs','sm','md','lg'].map(function(size){
1005             //Roo.log( size + ':' + settings[size]);
1006             
1007             if (settings[size+'off'] !== false) {
1008                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1009             }
1010             
1011             if (settings[size] === false) {
1012                 return;
1013             }
1014             
1015             if (!settings[size]) { // 0 = hidden
1016                 cfg.cls += ' hidden-' + size;
1017                 return;
1018             }
1019             cfg.cls += ' col-' + size + '-' + settings[size];
1020             
1021         });
1022         
1023         if (this.hidden) {
1024             cfg.cls += ' hidden';
1025         }
1026         
1027         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028             cfg.cls +=' alert alert-' + this.alert;
1029         }
1030         
1031         
1032         if (this.html.length) {
1033             cfg.html = this.html;
1034         }
1035         if (this.fa) {
1036             var fasize = '';
1037             if (this.fasize > 1) {
1038                 fasize = ' fa-' + this.fasize + 'x';
1039             }
1040             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1041             
1042             
1043         }
1044         if (this.icon) {
1045             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1046         }
1047         
1048         return cfg;
1049     }
1050    
1051 });
1052
1053  
1054
1055  /*
1056  * - LGPL
1057  *
1058  * page container.
1059  * 
1060  */
1061
1062
1063 /**
1064  * @class Roo.bootstrap.Container
1065  * @extends Roo.bootstrap.Component
1066  * Bootstrap Container class
1067  * @cfg {Boolean} jumbotron is it a jumbotron element
1068  * @cfg {String} html content of element
1069  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1070  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1071  * @cfg {String} header content of header (for panel)
1072  * @cfg {String} footer content of footer (for panel)
1073  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1074  * @cfg {String} tag (header|aside|section) type of HTML tag.
1075  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1076  * @cfg {String} fa font awesome icon
1077  * @cfg {String} icon (info-sign|check|...) glyphicon name
1078  * @cfg {Boolean} hidden (true|false) hide the element
1079  * @cfg {Boolean} expandable (true|false) default false
1080  * @cfg {Boolean} expanded (true|false) default true
1081  * @cfg {String} rheader contet on the right of header
1082  * @cfg {Boolean} clickable (true|false) default false
1083
1084  *     
1085  * @constructor
1086  * Create a new Container
1087  * @param {Object} config The config object
1088  */
1089
1090 Roo.bootstrap.Container = function(config){
1091     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1092     
1093     this.addEvents({
1094         // raw events
1095          /**
1096          * @event expand
1097          * After the panel has been expand
1098          * 
1099          * @param {Roo.bootstrap.Container} this
1100          */
1101         "expand" : true,
1102         /**
1103          * @event collapse
1104          * After the panel has been collapsed
1105          * 
1106          * @param {Roo.bootstrap.Container} this
1107          */
1108         "collapse" : true,
1109         /**
1110          * @event click
1111          * When a element is chick
1112          * @param {Roo.bootstrap.Container} this
1113          * @param {Roo.EventObject} e
1114          */
1115         "click" : true
1116     });
1117 };
1118
1119 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1120     
1121     jumbotron : false,
1122     well: '',
1123     panel : '',
1124     header: '',
1125     footer : '',
1126     sticky: '',
1127     tag : false,
1128     alert : false,
1129     fa: false,
1130     icon : false,
1131     expandable : false,
1132     rheader : '',
1133     expanded : true,
1134     clickable: false,
1135   
1136      
1137     getChildContainer : function() {
1138         
1139         if(!this.el){
1140             return false;
1141         }
1142         
1143         if (this.panel.length) {
1144             return this.el.select('.panel-body',true).first();
1145         }
1146         
1147         return this.el;
1148     },
1149     
1150     
1151     getAutoCreate : function(){
1152         
1153         var cfg = {
1154             tag : this.tag || 'div',
1155             html : '',
1156             cls : ''
1157         };
1158         if (this.jumbotron) {
1159             cfg.cls = 'jumbotron';
1160         }
1161         
1162         
1163         
1164         // - this is applied by the parent..
1165         //if (this.cls) {
1166         //    cfg.cls = this.cls + '';
1167         //}
1168         
1169         if (this.sticky.length) {
1170             
1171             var bd = Roo.get(document.body);
1172             if (!bd.hasClass('bootstrap-sticky')) {
1173                 bd.addClass('bootstrap-sticky');
1174                 Roo.select('html',true).setStyle('height', '100%');
1175             }
1176              
1177             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1178         }
1179         
1180         
1181         if (this.well.length) {
1182             switch (this.well) {
1183                 case 'lg':
1184                 case 'sm':
1185                     cfg.cls +=' well well-' +this.well;
1186                     break;
1187                 default:
1188                     cfg.cls +=' well';
1189                     break;
1190             }
1191         }
1192         
1193         if (this.hidden) {
1194             cfg.cls += ' hidden';
1195         }
1196         
1197         
1198         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1199             cfg.cls +=' alert alert-' + this.alert;
1200         }
1201         
1202         var body = cfg;
1203         
1204         if (this.panel.length) {
1205             cfg.cls += ' panel panel-' + this.panel;
1206             cfg.cn = [];
1207             if (this.header.length) {
1208                 
1209                 var h = [];
1210                 
1211                 if(this.expandable){
1212                     
1213                     cfg.cls = cfg.cls + ' expandable';
1214                     
1215                     h.push({
1216                         tag: 'i',
1217                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1218                     });
1219                     
1220                 }
1221                 
1222                 h.push(
1223                     {
1224                         tag: 'span',
1225                         cls : 'panel-title',
1226                         html : (this.expandable ? '&nbsp;' : '') + this.header
1227                     },
1228                     {
1229                         tag: 'span',
1230                         cls: 'panel-header-right',
1231                         html: this.rheader
1232                     }
1233                 );
1234                 
1235                 cfg.cn.push({
1236                     cls : 'panel-heading',
1237                     style : this.expandable ? 'cursor: pointer' : '',
1238                     cn : h
1239                 });
1240                 
1241             }
1242             
1243             body = false;
1244             cfg.cn.push({
1245                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1246                 html : this.html
1247             });
1248             
1249             
1250             if (this.footer.length) {
1251                 cfg.cn.push({
1252                     cls : 'panel-footer',
1253                     html : this.footer
1254                     
1255                 });
1256             }
1257             
1258         }
1259         
1260         if (body) {
1261             body.html = this.html || cfg.html;
1262             // prefix with the icons..
1263             if (this.fa) {
1264                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1265             }
1266             if (this.icon) {
1267                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1268             }
1269             
1270             
1271         }
1272         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1273             cfg.cls =  'container';
1274         }
1275         
1276         return cfg;
1277     },
1278     
1279     initEvents: function() 
1280     {
1281         if(this.expandable){
1282             var headerEl = this.headerEl();
1283         
1284             if(headerEl){
1285                 headerEl.on('click', this.onToggleClick, this);
1286             }
1287         }
1288         
1289         if(this.clickable){
1290             this.el.on('click', this.onClick, this);
1291         }
1292         
1293     },
1294     
1295     onToggleClick : function()
1296     {
1297         var headerEl = this.headerEl();
1298         
1299         if(!headerEl){
1300             return;
1301         }
1302         
1303         if(this.expanded){
1304             this.collapse();
1305             return;
1306         }
1307         
1308         this.expand();
1309     },
1310     
1311     expand : function()
1312     {
1313         if(this.fireEvent('expand', this)) {
1314             
1315             this.expanded = true;
1316             
1317             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1318             
1319             this.el.select('.panel-body',true).first().removeClass('hide');
1320             
1321             var toggleEl = this.toggleEl();
1322
1323             if(!toggleEl){
1324                 return;
1325             }
1326
1327             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1328         }
1329         
1330     },
1331     
1332     collapse : function()
1333     {
1334         if(this.fireEvent('collapse', this)) {
1335             
1336             this.expanded = false;
1337             
1338             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1339             this.el.select('.panel-body',true).first().addClass('hide');
1340         
1341             var toggleEl = this.toggleEl();
1342
1343             if(!toggleEl){
1344                 return;
1345             }
1346
1347             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1348         }
1349     },
1350     
1351     toggleEl : function()
1352     {
1353         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1354             return;
1355         }
1356         
1357         return this.el.select('.panel-heading .fa',true).first();
1358     },
1359     
1360     headerEl : function()
1361     {
1362         if(!this.el || !this.panel.length || !this.header.length){
1363             return;
1364         }
1365         
1366         return this.el.select('.panel-heading',true).first()
1367     },
1368     
1369     bodyEl : function()
1370     {
1371         if(!this.el || !this.panel.length){
1372             return;
1373         }
1374         
1375         return this.el.select('.panel-body',true).first()
1376     },
1377     
1378     titleEl : function()
1379     {
1380         if(!this.el || !this.panel.length || !this.header.length){
1381             return;
1382         }
1383         
1384         return this.el.select('.panel-title',true).first();
1385     },
1386     
1387     setTitle : function(v)
1388     {
1389         var titleEl = this.titleEl();
1390         
1391         if(!titleEl){
1392             return;
1393         }
1394         
1395         titleEl.dom.innerHTML = v;
1396     },
1397     
1398     getTitle : function()
1399     {
1400         
1401         var titleEl = this.titleEl();
1402         
1403         if(!titleEl){
1404             return '';
1405         }
1406         
1407         return titleEl.dom.innerHTML;
1408     },
1409     
1410     setRightTitle : function(v)
1411     {
1412         var t = this.el.select('.panel-header-right',true).first();
1413         
1414         if(!t){
1415             return;
1416         }
1417         
1418         t.dom.innerHTML = v;
1419     },
1420     
1421     onClick : function(e)
1422     {
1423         e.preventDefault();
1424         
1425         this.fireEvent('click', this, e);
1426     }
1427 });
1428
1429  /*
1430  * - LGPL
1431  *
1432  * image
1433  * 
1434  */
1435
1436
1437 /**
1438  * @class Roo.bootstrap.Img
1439  * @extends Roo.bootstrap.Component
1440  * Bootstrap Img class
1441  * @cfg {Boolean} imgResponsive false | true
1442  * @cfg {String} border rounded | circle | thumbnail
1443  * @cfg {String} src image source
1444  * @cfg {String} alt image alternative text
1445  * @cfg {String} href a tag href
1446  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1447  * @cfg {String} xsUrl xs image source
1448  * @cfg {String} smUrl sm image source
1449  * @cfg {String} mdUrl md image source
1450  * @cfg {String} lgUrl lg image source
1451  * 
1452  * @constructor
1453  * Create a new Input
1454  * @param {Object} config The config object
1455  */
1456
1457 Roo.bootstrap.Img = function(config){
1458     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1459     
1460     this.addEvents({
1461         // img events
1462         /**
1463          * @event click
1464          * The img click event for the img.
1465          * @param {Roo.EventObject} e
1466          */
1467         "click" : true
1468     });
1469 };
1470
1471 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1472     
1473     imgResponsive: true,
1474     border: '',
1475     src: 'about:blank',
1476     href: false,
1477     target: false,
1478     xsUrl: '',
1479     smUrl: '',
1480     mdUrl: '',
1481     lgUrl: '',
1482
1483     getAutoCreate : function()
1484     {   
1485         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1486             return this.createSingleImg();
1487         }
1488         
1489         var cfg = {
1490             tag: 'div',
1491             cls: 'roo-image-responsive-group',
1492             cn: []
1493         };
1494         var _this = this;
1495         
1496         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1497             
1498             if(!_this[size + 'Url']){
1499                 return;
1500             }
1501             
1502             var img = {
1503                 tag: 'img',
1504                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1505                 html: _this.html || cfg.html,
1506                 src: _this[size + 'Url']
1507             };
1508             
1509             img.cls += ' roo-image-responsive-' + size;
1510             
1511             var s = ['xs', 'sm', 'md', 'lg'];
1512             
1513             s.splice(s.indexOf(size), 1);
1514             
1515             Roo.each(s, function(ss){
1516                 img.cls += ' hidden-' + ss;
1517             });
1518             
1519             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1520                 cfg.cls += ' img-' + _this.border;
1521             }
1522             
1523             if(_this.alt){
1524                 cfg.alt = _this.alt;
1525             }
1526             
1527             if(_this.href){
1528                 var a = {
1529                     tag: 'a',
1530                     href: _this.href,
1531                     cn: [
1532                         img
1533                     ]
1534                 };
1535
1536                 if(this.target){
1537                     a.target = _this.target;
1538                 }
1539             }
1540             
1541             cfg.cn.push((_this.href) ? a : img);
1542             
1543         });
1544         
1545         return cfg;
1546     },
1547     
1548     createSingleImg : function()
1549     {
1550         var cfg = {
1551             tag: 'img',
1552             cls: (this.imgResponsive) ? 'img-responsive' : '',
1553             html : null,
1554             src : 'about:blank'  // just incase src get's set to undefined?!?
1555         };
1556         
1557         cfg.html = this.html || cfg.html;
1558         
1559         cfg.src = this.src || cfg.src;
1560         
1561         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1562             cfg.cls += ' img-' + this.border;
1563         }
1564         
1565         if(this.alt){
1566             cfg.alt = this.alt;
1567         }
1568         
1569         if(this.href){
1570             var a = {
1571                 tag: 'a',
1572                 href: this.href,
1573                 cn: [
1574                     cfg
1575                 ]
1576             };
1577             
1578             if(this.target){
1579                 a.target = this.target;
1580             }
1581             
1582         }
1583         
1584         return (this.href) ? a : cfg;
1585     },
1586     
1587     initEvents: function() 
1588     {
1589         if(!this.href){
1590             this.el.on('click', this.onClick, this);
1591         }
1592         
1593     },
1594     
1595     onClick : function(e)
1596     {
1597         Roo.log('img onclick');
1598         this.fireEvent('click', this, e);
1599     },
1600     /**
1601      * Sets the url of the image - used to update it
1602      * @param {String} url the url of the image
1603      */
1604     
1605     setSrc : function(url)
1606     {
1607         this.src =  url;
1608         
1609         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1610             this.el.dom.src =  url;
1611             return;
1612         }
1613         
1614         this.el.select('img', true).first().dom.src =  url;
1615     }
1616     
1617     
1618    
1619 });
1620
1621  /*
1622  * - LGPL
1623  *
1624  * image
1625  * 
1626  */
1627
1628
1629 /**
1630  * @class Roo.bootstrap.Link
1631  * @extends Roo.bootstrap.Component
1632  * Bootstrap Link Class
1633  * @cfg {String} alt image alternative text
1634  * @cfg {String} href a tag href
1635  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1636  * @cfg {String} html the content of the link.
1637  * @cfg {String} anchor name for the anchor link
1638  * @cfg {String} fa - favicon
1639
1640  * @cfg {Boolean} preventDefault (true | false) default false
1641
1642  * 
1643  * @constructor
1644  * Create a new Input
1645  * @param {Object} config The config object
1646  */
1647
1648 Roo.bootstrap.Link = function(config){
1649     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1650     
1651     this.addEvents({
1652         // img events
1653         /**
1654          * @event click
1655          * The img click event for the img.
1656          * @param {Roo.EventObject} e
1657          */
1658         "click" : true
1659     });
1660 };
1661
1662 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1663     
1664     href: false,
1665     target: false,
1666     preventDefault: false,
1667     anchor : false,
1668     alt : false,
1669     fa: false,
1670
1671
1672     getAutoCreate : function()
1673     {
1674         var html = this.html || '';
1675         
1676         if (this.fa !== false) {
1677             html = '<i class="fa fa-' + this.fa + '"></i>';
1678         }
1679         var cfg = {
1680             tag: 'a'
1681         };
1682         // anchor's do not require html/href...
1683         if (this.anchor === false) {
1684             cfg.html = html;
1685             cfg.href = this.href || '#';
1686         } else {
1687             cfg.name = this.anchor;
1688             if (this.html !== false || this.fa !== false) {
1689                 cfg.html = html;
1690             }
1691             if (this.href !== false) {
1692                 cfg.href = this.href;
1693             }
1694         }
1695         
1696         if(this.alt !== false){
1697             cfg.alt = this.alt;
1698         }
1699         
1700         
1701         if(this.target !== false) {
1702             cfg.target = this.target;
1703         }
1704         
1705         return cfg;
1706     },
1707     
1708     initEvents: function() {
1709         
1710         if(!this.href || this.preventDefault){
1711             this.el.on('click', this.onClick, this);
1712         }
1713     },
1714     
1715     onClick : function(e)
1716     {
1717         if(this.preventDefault){
1718             e.preventDefault();
1719         }
1720         //Roo.log('img onclick');
1721         this.fireEvent('click', this, e);
1722     }
1723    
1724 });
1725
1726  /*
1727  * - LGPL
1728  *
1729  * header
1730  * 
1731  */
1732
1733 /**
1734  * @class Roo.bootstrap.Header
1735  * @extends Roo.bootstrap.Component
1736  * Bootstrap Header class
1737  * @cfg {String} html content of header
1738  * @cfg {Number} level (1|2|3|4|5|6) default 1
1739  * 
1740  * @constructor
1741  * Create a new Header
1742  * @param {Object} config The config object
1743  */
1744
1745
1746 Roo.bootstrap.Header  = function(config){
1747     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1748 };
1749
1750 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1751     
1752     //href : false,
1753     html : false,
1754     level : 1,
1755     
1756     
1757     
1758     getAutoCreate : function(){
1759         
1760         
1761         
1762         var cfg = {
1763             tag: 'h' + (1 *this.level),
1764             html: this.html || ''
1765         } ;
1766         
1767         return cfg;
1768     }
1769    
1770 });
1771
1772  
1773
1774  /*
1775  * Based on:
1776  * Ext JS Library 1.1.1
1777  * Copyright(c) 2006-2007, Ext JS, LLC.
1778  *
1779  * Originally Released Under LGPL - original licence link has changed is not relivant.
1780  *
1781  * Fork - LGPL
1782  * <script type="text/javascript">
1783  */
1784  
1785 /**
1786  * @class Roo.bootstrap.MenuMgr
1787  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1788  * @singleton
1789  */
1790 Roo.bootstrap.MenuMgr = function(){
1791    var menus, active, groups = {}, attached = false, lastShow = new Date();
1792
1793    // private - called when first menu is created
1794    function init(){
1795        menus = {};
1796        active = new Roo.util.MixedCollection();
1797        Roo.get(document).addKeyListener(27, function(){
1798            if(active.length > 0){
1799                hideAll();
1800            }
1801        });
1802    }
1803
1804    // private
1805    function hideAll(){
1806        if(active && active.length > 0){
1807            var c = active.clone();
1808            c.each(function(m){
1809                m.hide();
1810            });
1811        }
1812    }
1813
1814    // private
1815    function onHide(m){
1816        active.remove(m);
1817        if(active.length < 1){
1818            Roo.get(document).un("mouseup", onMouseDown);
1819             
1820            attached = false;
1821        }
1822    }
1823
1824    // private
1825    function onShow(m){
1826        var last = active.last();
1827        lastShow = new Date();
1828        active.add(m);
1829        if(!attached){
1830           Roo.get(document).on("mouseup", onMouseDown);
1831            
1832            attached = true;
1833        }
1834        if(m.parentMenu){
1835           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1836           m.parentMenu.activeChild = m;
1837        }else if(last && last.isVisible()){
1838           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1839        }
1840    }
1841
1842    // private
1843    function onBeforeHide(m){
1844        if(m.activeChild){
1845            m.activeChild.hide();
1846        }
1847        if(m.autoHideTimer){
1848            clearTimeout(m.autoHideTimer);
1849            delete m.autoHideTimer;
1850        }
1851    }
1852
1853    // private
1854    function onBeforeShow(m){
1855        var pm = m.parentMenu;
1856        if(!pm && !m.allowOtherMenus){
1857            hideAll();
1858        }else if(pm && pm.activeChild && active != m){
1859            pm.activeChild.hide();
1860        }
1861    }
1862
1863    // private this should really trigger on mouseup..
1864    function onMouseDown(e){
1865         Roo.log("on Mouse Up");
1866         
1867         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1868             Roo.log("MenuManager hideAll");
1869             hideAll();
1870             e.stopEvent();
1871         }
1872         
1873         
1874    }
1875
1876    // private
1877    function onBeforeCheck(mi, state){
1878        if(state){
1879            var g = groups[mi.group];
1880            for(var i = 0, l = g.length; i < l; i++){
1881                if(g[i] != mi){
1882                    g[i].setChecked(false);
1883                }
1884            }
1885        }
1886    }
1887
1888    return {
1889
1890        /**
1891         * Hides all menus that are currently visible
1892         */
1893        hideAll : function(){
1894             hideAll();  
1895        },
1896
1897        // private
1898        register : function(menu){
1899            if(!menus){
1900                init();
1901            }
1902            menus[menu.id] = menu;
1903            menu.on("beforehide", onBeforeHide);
1904            menu.on("hide", onHide);
1905            menu.on("beforeshow", onBeforeShow);
1906            menu.on("show", onShow);
1907            var g = menu.group;
1908            if(g && menu.events["checkchange"]){
1909                if(!groups[g]){
1910                    groups[g] = [];
1911                }
1912                groups[g].push(menu);
1913                menu.on("checkchange", onCheck);
1914            }
1915        },
1916
1917         /**
1918          * Returns a {@link Roo.menu.Menu} object
1919          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1920          * be used to generate and return a new Menu instance.
1921          */
1922        get : function(menu){
1923            if(typeof menu == "string"){ // menu id
1924                return menus[menu];
1925            }else if(menu.events){  // menu instance
1926                return menu;
1927            }
1928            /*else if(typeof menu.length == 'number'){ // array of menu items?
1929                return new Roo.bootstrap.Menu({items:menu});
1930            }else{ // otherwise, must be a config
1931                return new Roo.bootstrap.Menu(menu);
1932            }
1933            */
1934            return false;
1935        },
1936
1937        // private
1938        unregister : function(menu){
1939            delete menus[menu.id];
1940            menu.un("beforehide", onBeforeHide);
1941            menu.un("hide", onHide);
1942            menu.un("beforeshow", onBeforeShow);
1943            menu.un("show", onShow);
1944            var g = menu.group;
1945            if(g && menu.events["checkchange"]){
1946                groups[g].remove(menu);
1947                menu.un("checkchange", onCheck);
1948            }
1949        },
1950
1951        // private
1952        registerCheckable : function(menuItem){
1953            var g = menuItem.group;
1954            if(g){
1955                if(!groups[g]){
1956                    groups[g] = [];
1957                }
1958                groups[g].push(menuItem);
1959                menuItem.on("beforecheckchange", onBeforeCheck);
1960            }
1961        },
1962
1963        // private
1964        unregisterCheckable : function(menuItem){
1965            var g = menuItem.group;
1966            if(g){
1967                groups[g].remove(menuItem);
1968                menuItem.un("beforecheckchange", onBeforeCheck);
1969            }
1970        }
1971    };
1972 }();/*
1973  * - LGPL
1974  *
1975  * menu
1976  * 
1977  */
1978
1979 /**
1980  * @class Roo.bootstrap.Menu
1981  * @extends Roo.bootstrap.Component
1982  * Bootstrap Menu class - container for MenuItems
1983  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1984  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1985  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1986  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1987  * 
1988  * @constructor
1989  * Create a new Menu
1990  * @param {Object} config The config object
1991  */
1992
1993
1994 Roo.bootstrap.Menu = function(config){
1995     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1996     if (this.registerMenu && this.type != 'treeview')  {
1997         Roo.bootstrap.MenuMgr.register(this);
1998     }
1999     this.addEvents({
2000         /**
2001          * @event beforeshow
2002          * Fires before this menu is displayed
2003          * @param {Roo.menu.Menu} this
2004          */
2005         beforeshow : true,
2006         /**
2007          * @event beforehide
2008          * Fires before this menu is hidden
2009          * @param {Roo.menu.Menu} this
2010          */
2011         beforehide : true,
2012         /**
2013          * @event show
2014          * Fires after this menu is displayed
2015          * @param {Roo.menu.Menu} this
2016          */
2017         show : true,
2018         /**
2019          * @event hide
2020          * Fires after this menu is hidden
2021          * @param {Roo.menu.Menu} this
2022          */
2023         hide : true,
2024         /**
2025          * @event click
2026          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2027          * @param {Roo.menu.Menu} this
2028          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2029          * @param {Roo.EventObject} e
2030          */
2031         click : true,
2032         /**
2033          * @event mouseover
2034          * Fires when the mouse is hovering over this menu
2035          * @param {Roo.menu.Menu} this
2036          * @param {Roo.EventObject} e
2037          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038          */
2039         mouseover : true,
2040         /**
2041          * @event mouseout
2042          * Fires when the mouse exits this menu
2043          * @param {Roo.menu.Menu} this
2044          * @param {Roo.EventObject} e
2045          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2046          */
2047         mouseout : true,
2048         /**
2049          * @event itemclick
2050          * Fires when a menu item contained in this menu is clicked
2051          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2052          * @param {Roo.EventObject} e
2053          */
2054         itemclick: true
2055     });
2056     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2057 };
2058
2059 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2060     
2061    /// html : false,
2062     //align : '',
2063     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2064     type: false,
2065     /**
2066      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2067      */
2068     registerMenu : true,
2069     
2070     menuItems :false, // stores the menu items..
2071     
2072     hidden:true,
2073         
2074     parentMenu : false,
2075     
2076     stopEvent : true,
2077     
2078     isLink : false,
2079     
2080     getChildContainer : function() {
2081         return this.el;  
2082     },
2083     
2084     getAutoCreate : function(){
2085          
2086         //if (['right'].indexOf(this.align)!==-1) {
2087         //    cfg.cn[1].cls += ' pull-right'
2088         //}
2089         
2090         
2091         var cfg = {
2092             tag : 'ul',
2093             cls : 'dropdown-menu' ,
2094             style : 'z-index:1000'
2095             
2096         };
2097         
2098         if (this.type === 'submenu') {
2099             cfg.cls = 'submenu active';
2100         }
2101         if (this.type === 'treeview') {
2102             cfg.cls = 'treeview-menu';
2103         }
2104         
2105         return cfg;
2106     },
2107     initEvents : function() {
2108         
2109        // Roo.log("ADD event");
2110        // Roo.log(this.triggerEl.dom);
2111         
2112         this.triggerEl.on('click', this.onTriggerClick, this);
2113         
2114         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2115         
2116         this.triggerEl.addClass('dropdown-toggle');
2117         
2118         if (Roo.isTouch) {
2119             this.el.on('touchstart'  , this.onTouch, this);
2120         }
2121         this.el.on('click' , this.onClick, this);
2122
2123         this.el.on("mouseover", this.onMouseOver, this);
2124         this.el.on("mouseout", this.onMouseOut, this);
2125         
2126     },
2127     
2128     findTargetItem : function(e)
2129     {
2130         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2131         if(!t){
2132             return false;
2133         }
2134         //Roo.log(t);         Roo.log(t.id);
2135         if(t && t.id){
2136             //Roo.log(this.menuitems);
2137             return this.menuitems.get(t.id);
2138             
2139             //return this.items.get(t.menuItemId);
2140         }
2141         
2142         return false;
2143     },
2144     
2145     onTouch : function(e) 
2146     {
2147         Roo.log("menu.onTouch");
2148         //e.stopEvent(); this make the user popdown broken
2149         this.onClick(e);
2150     },
2151     
2152     onClick : function(e)
2153     {
2154         Roo.log("menu.onClick");
2155         
2156         var t = this.findTargetItem(e);
2157         if(!t || t.isContainer){
2158             return;
2159         }
2160         Roo.log(e);
2161         /*
2162         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2163             if(t == this.activeItem && t.shouldDeactivate(e)){
2164                 this.activeItem.deactivate();
2165                 delete this.activeItem;
2166                 return;
2167             }
2168             if(t.canActivate){
2169                 this.setActiveItem(t, true);
2170             }
2171             return;
2172             
2173             
2174         }
2175         */
2176        
2177         Roo.log('pass click event');
2178         
2179         t.onClick(e);
2180         
2181         this.fireEvent("click", this, t, e);
2182         
2183         var _this = this;
2184         
2185         if(!t.href.length || t.href == '#'){
2186             (function() { _this.hide(); }).defer(100);
2187         }
2188         
2189     },
2190     
2191     onMouseOver : function(e){
2192         var t  = this.findTargetItem(e);
2193         //Roo.log(t);
2194         //if(t){
2195         //    if(t.canActivate && !t.disabled){
2196         //        this.setActiveItem(t, true);
2197         //    }
2198         //}
2199         
2200         this.fireEvent("mouseover", this, e, t);
2201     },
2202     isVisible : function(){
2203         return !this.hidden;
2204     },
2205      onMouseOut : function(e){
2206         var t  = this.findTargetItem(e);
2207         
2208         //if(t ){
2209         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2210         //        this.activeItem.deactivate();
2211         //        delete this.activeItem;
2212         //    }
2213         //}
2214         this.fireEvent("mouseout", this, e, t);
2215     },
2216     
2217     
2218     /**
2219      * Displays this menu relative to another element
2220      * @param {String/HTMLElement/Roo.Element} element The element to align to
2221      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2222      * the element (defaults to this.defaultAlign)
2223      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2224      */
2225     show : function(el, pos, parentMenu){
2226         this.parentMenu = parentMenu;
2227         if(!this.el){
2228             this.render();
2229         }
2230         this.fireEvent("beforeshow", this);
2231         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2232     },
2233      /**
2234      * Displays this menu at a specific xy position
2235      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2236      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2237      */
2238     showAt : function(xy, parentMenu, /* private: */_e){
2239         this.parentMenu = parentMenu;
2240         if(!this.el){
2241             this.render();
2242         }
2243         if(_e !== false){
2244             this.fireEvent("beforeshow", this);
2245             //xy = this.el.adjustForConstraints(xy);
2246         }
2247         
2248         //this.el.show();
2249         this.hideMenuItems();
2250         this.hidden = false;
2251         this.triggerEl.addClass('open');
2252         
2253         // reassign x when hitting right
2254         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2256         }
2257         
2258         // reassign y when hitting bottom
2259         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2260             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2261         }
2262         
2263         // but the list may align on trigger left or trigger top... should it be a properity?
2264         
2265         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2266             this.el.setXY(xy);
2267         }
2268         
2269         this.focus();
2270         this.fireEvent("show", this);
2271     },
2272     
2273     focus : function(){
2274         return;
2275         if(!this.hidden){
2276             this.doFocus.defer(50, this);
2277         }
2278     },
2279
2280     doFocus : function(){
2281         if(!this.hidden){
2282             this.focusEl.focus();
2283         }
2284     },
2285
2286     /**
2287      * Hides this menu and optionally all parent menus
2288      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2289      */
2290     hide : function(deep)
2291     {
2292         
2293         this.hideMenuItems();
2294         if(this.el && this.isVisible()){
2295             this.fireEvent("beforehide", this);
2296             if(this.activeItem){
2297                 this.activeItem.deactivate();
2298                 this.activeItem = null;
2299             }
2300             this.triggerEl.removeClass('open');;
2301             this.hidden = true;
2302             this.fireEvent("hide", this);
2303         }
2304         if(deep === true && this.parentMenu){
2305             this.parentMenu.hide(true);
2306         }
2307     },
2308     
2309     onTriggerClick : function(e)
2310     {
2311         Roo.log('trigger click');
2312         
2313         var target = e.getTarget();
2314         
2315         Roo.log(target.nodeName.toLowerCase());
2316         
2317         if(target.nodeName.toLowerCase() === 'i'){
2318             e.preventDefault();
2319         }
2320         
2321     },
2322     
2323     onTriggerPress  : function(e)
2324     {
2325         Roo.log('trigger press');
2326         //Roo.log(e.getTarget());
2327        // Roo.log(this.triggerEl.dom);
2328        
2329         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330         var pel = Roo.get(e.getTarget());
2331         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332             Roo.log('is treeview or dropdown?');
2333             return;
2334         }
2335         
2336         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2337             return;
2338         }
2339         
2340         if (this.isVisible()) {
2341             Roo.log('hide');
2342             this.hide();
2343         } else {
2344             Roo.log('show');
2345             this.show(this.triggerEl, false, false);
2346         }
2347         
2348         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2349             e.stopEvent();
2350         }
2351         
2352     },
2353        
2354     
2355     hideMenuItems : function()
2356     {
2357         Roo.log("hide Menu Items");
2358         if (!this.el) { 
2359             return;
2360         }
2361         //$(backdrop).remove()
2362         this.el.select('.open',true).each(function(aa) {
2363             
2364             aa.removeClass('open');
2365           //var parent = getParent($(this))
2366           //var relatedTarget = { relatedTarget: this }
2367           
2368            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369           //if (e.isDefaultPrevented()) return
2370            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2371         });
2372     },
2373     addxtypeChild : function (tree, cntr) {
2374         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2375           
2376         this.menuitems.add(comp);
2377         return comp;
2378
2379     },
2380     getEl : function()
2381     {
2382         Roo.log(this.el);
2383         return this.el;
2384     },
2385     
2386     clear : function()
2387     {
2388         this.getEl().dom.innerHTML = '';
2389         this.menuitems.clear();
2390     }
2391 });
2392
2393  
2394  /*
2395  * - LGPL
2396  *
2397  * menu item
2398  * 
2399  */
2400
2401
2402 /**
2403  * @class Roo.bootstrap.MenuItem
2404  * @extends Roo.bootstrap.Component
2405  * Bootstrap MenuItem class
2406  * @cfg {String} html the menu label
2407  * @cfg {String} href the link
2408  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2411  * @cfg {String} fa favicon to show on left of menu item.
2412  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2413  * 
2414  * 
2415  * @constructor
2416  * Create a new MenuItem
2417  * @param {Object} config The config object
2418  */
2419
2420
2421 Roo.bootstrap.MenuItem = function(config){
2422     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2423     this.addEvents({
2424         // raw events
2425         /**
2426          * @event click
2427          * The raw click event for the entire grid.
2428          * @param {Roo.bootstrap.MenuItem} this
2429          * @param {Roo.EventObject} e
2430          */
2431         "click" : true
2432     });
2433 };
2434
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2436     
2437     href : false,
2438     html : false,
2439     preventDefault: false,
2440     isContainer : false,
2441     active : false,
2442     fa: false,
2443     
2444     getAutoCreate : function(){
2445         
2446         if(this.isContainer){
2447             return {
2448                 tag: 'li',
2449                 cls: 'dropdown-menu-item'
2450             };
2451         }
2452         var ctag = {
2453             tag: 'span',
2454             html: 'Link'
2455         };
2456         
2457         var anc = {
2458             tag : 'a',
2459             href : '#',
2460             cn : [  ]
2461         };
2462         
2463         if (this.fa !== false) {
2464             anc.cn.push({
2465                 tag : 'i',
2466                 cls : 'fa fa-' + this.fa
2467             });
2468         }
2469         
2470         anc.cn.push(ctag);
2471         
2472         
2473         var cfg= {
2474             tag: 'li',
2475             cls: 'dropdown-menu-item',
2476             cn: [ anc ]
2477         };
2478         if (this.parent().type == 'treeview') {
2479             cfg.cls = 'treeview-menu';
2480         }
2481         if (this.active) {
2482             cfg.cls += ' active';
2483         }
2484         
2485         
2486         
2487         anc.href = this.href || cfg.cn[0].href ;
2488         ctag.html = this.html || cfg.cn[0].html ;
2489         return cfg;
2490     },
2491     
2492     initEvents: function()
2493     {
2494         if (this.parent().type == 'treeview') {
2495             this.el.select('a').on('click', this.onClick, this);
2496         }
2497         
2498         if (this.menu) {
2499             this.menu.parentType = this.xtype;
2500             this.menu.triggerEl = this.el;
2501             this.menu = this.addxtype(Roo.apply({}, this.menu));
2502         }
2503         
2504     },
2505     onClick : function(e)
2506     {
2507         Roo.log('item on click ');
2508         
2509         if(this.preventDefault){
2510             e.preventDefault();
2511         }
2512         //this.parent().hideMenuItems();
2513         
2514         this.fireEvent('click', this, e);
2515     },
2516     getEl : function()
2517     {
2518         return this.el;
2519     } 
2520 });
2521
2522  
2523
2524  /*
2525  * - LGPL
2526  *
2527  * menu separator
2528  * 
2529  */
2530
2531
2532 /**
2533  * @class Roo.bootstrap.MenuSeparator
2534  * @extends Roo.bootstrap.Component
2535  * Bootstrap MenuSeparator class
2536  * 
2537  * @constructor
2538  * Create a new MenuItem
2539  * @param {Object} config The config object
2540  */
2541
2542
2543 Roo.bootstrap.MenuSeparator = function(config){
2544     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2545 };
2546
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2548     
2549     getAutoCreate : function(){
2550         var cfg = {
2551             cls: 'divider',
2552             tag : 'li'
2553         };
2554         
2555         return cfg;
2556     }
2557    
2558 });
2559
2560  
2561
2562  
2563 /*
2564 * Licence: LGPL
2565 */
2566
2567 /**
2568  * @class Roo.bootstrap.Modal
2569  * @extends Roo.bootstrap.Component
2570  * Bootstrap Modal class
2571  * @cfg {String} title Title of dialog
2572  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2574  * @cfg {Boolean} specificTitle default false
2575  * @cfg {Array} buttons Array of buttons or standard button set..
2576  * @cfg {String} buttonPosition (left|right|center) default right
2577  * @cfg {Boolean} animate default true
2578  * @cfg {Boolean} allow_close default true
2579  * @cfg {Boolean} fitwindow default false
2580  * @cfg {String} size (sm|lg) default empty
2581  * @cfg {Number} max_width set the max width of modal
2582  *
2583  *
2584  * @constructor
2585  * Create a new Modal Dialog
2586  * @param {Object} config The config object
2587  */
2588
2589 Roo.bootstrap.Modal = function(config){
2590     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2591     this.addEvents({
2592         // raw events
2593         /**
2594          * @event btnclick
2595          * The raw btnclick event for the button
2596          * @param {Roo.EventObject} e
2597          */
2598         "btnclick" : true,
2599         /**
2600          * @event resize
2601          * Fire when dialog resize
2602          * @param {Roo.bootstrap.Modal} this
2603          * @param {Roo.EventObject} e
2604          */
2605         "resize" : true
2606     });
2607     this.buttons = this.buttons || [];
2608
2609     if (this.tmpl) {
2610         this.tmpl = Roo.factory(this.tmpl);
2611     }
2612
2613 };
2614
2615 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2616
2617     title : 'test dialog',
2618
2619     buttons : false,
2620
2621     // set on load...
2622
2623     html: false,
2624
2625     tmp: false,
2626
2627     specificTitle: false,
2628
2629     buttonPosition: 'right',
2630
2631     allow_close : true,
2632
2633     animate : true,
2634
2635     fitwindow: false,
2636
2637
2638      // private
2639     dialogEl: false,
2640     bodyEl:  false,
2641     footerEl:  false,
2642     titleEl:  false,
2643     closeEl:  false,
2644
2645     size: '',
2646     
2647     max_width: 0,
2648
2649
2650     onRender : function(ct, position)
2651     {
2652         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2653
2654         if(!this.el){
2655             var cfg = Roo.apply({},  this.getAutoCreate());
2656             cfg.id = Roo.id();
2657             //if(!cfg.name){
2658             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2659             //}
2660             //if (!cfg.name.length) {
2661             //    delete cfg.name;
2662            // }
2663             if (this.cls) {
2664                 cfg.cls += ' ' + this.cls;
2665             }
2666             if (this.style) {
2667                 cfg.style = this.style;
2668             }
2669             this.el = Roo.get(document.body).createChild(cfg, position);
2670         }
2671         //var type = this.el.dom.type;
2672
2673
2674         if(this.tabIndex !== undefined){
2675             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2676         }
2677
2678         this.dialogEl = this.el.select('.modal-dialog',true).first();
2679         this.bodyEl = this.el.select('.modal-body',true).first();
2680         this.closeEl = this.el.select('.modal-header .close', true).first();
2681         this.headerEl = this.el.select('.modal-header',true).first();
2682         this.titleEl = this.el.select('.modal-title',true).first();
2683         this.footerEl = this.el.select('.modal-footer',true).first();
2684
2685         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2686         
2687         //this.el.addClass("x-dlg-modal");
2688
2689         if (this.buttons.length) {
2690             Roo.each(this.buttons, function(bb) {
2691                 var b = Roo.apply({}, bb);
2692                 b.xns = b.xns || Roo.bootstrap;
2693                 b.xtype = b.xtype || 'Button';
2694                 if (typeof(b.listeners) == 'undefined') {
2695                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2696                 }
2697
2698                 var btn = Roo.factory(b);
2699
2700                 btn.render(this.el.select('.modal-footer div').first());
2701
2702             },this);
2703         }
2704         // render the children.
2705         var nitems = [];
2706
2707         if(typeof(this.items) != 'undefined'){
2708             var items = this.items;
2709             delete this.items;
2710
2711             for(var i =0;i < items.length;i++) {
2712                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2713             }
2714         }
2715
2716         this.items = nitems;
2717
2718         // where are these used - they used to be body/close/footer
2719
2720
2721         this.initEvents();
2722         //this.el.addClass([this.fieldClass, this.cls]);
2723
2724     },
2725
2726     getAutoCreate : function()
2727     {
2728         var bdy = {
2729                 cls : 'modal-body',
2730                 html : this.html || ''
2731         };
2732
2733         var title = {
2734             tag: 'h4',
2735             cls : 'modal-title',
2736             html : this.title
2737         };
2738
2739         if(this.specificTitle){
2740             title = this.title;
2741
2742         };
2743
2744         var header = [];
2745         if (this.allow_close) {
2746             header.push({
2747                 tag: 'button',
2748                 cls : 'close',
2749                 html : '&times'
2750             });
2751         }
2752
2753         header.push(title);
2754
2755         var size = '';
2756
2757         if(this.size.length){
2758             size = 'modal-' + this.size;
2759         }
2760
2761         var modal = {
2762             cls: "modal",
2763              cn : [
2764                 {
2765                     cls: "modal-dialog " + size,
2766                     cn : [
2767                         {
2768                             cls : "modal-content",
2769                             cn : [
2770                                 {
2771                                     cls : 'modal-header',
2772                                     cn : header
2773                                 },
2774                                 bdy,
2775                                 {
2776                                     cls : 'modal-footer',
2777                                     cn : [
2778                                         {
2779                                             tag: 'div',
2780                                             cls: 'btn-' + this.buttonPosition
2781                                         }
2782                                     ]
2783
2784                                 }
2785
2786
2787                             ]
2788
2789                         }
2790                     ]
2791
2792                 }
2793             ]
2794         };
2795
2796         if(this.animate){
2797             modal.cls += ' fade';
2798         }
2799
2800         return modal;
2801
2802     },
2803     getChildContainer : function() {
2804
2805          return this.bodyEl;
2806
2807     },
2808     getButtonContainer : function() {
2809          return this.el.select('.modal-footer div',true).first();
2810
2811     },
2812     initEvents : function()
2813     {
2814         if (this.allow_close) {
2815             this.closeEl.on('click', this.hide, this);
2816         }
2817         Roo.EventManager.onWindowResize(this.resize, this, true);
2818
2819
2820     },
2821
2822     resize : function()
2823     {
2824         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2825         
2826         if (this.fitwindow) {
2827             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2828             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2829             this.setSize(w,h);
2830         }
2831         
2832         if(!this.fitwindow && this.max_width !== 0){
2833             
2834             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2835             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2836             this.setSize(w,h);
2837         }
2838         
2839     },
2840
2841     setSize : function(w,h)
2842     {
2843         if (!w && !h) {
2844             return;
2845         }
2846         this.resizeTo(w,h);
2847     },
2848
2849     show : function() {
2850
2851         if (!this.rendered) {
2852             this.render();
2853         }
2854
2855         //this.el.setStyle('display', 'block');
2856         this.el.removeClass('hideing');        
2857         this.el.addClass('show');
2858  
2859         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2860             var _this = this;
2861             (function(){
2862                 this.el.addClass('in');
2863             }).defer(50, this);
2864         }else{
2865             this.el.addClass('in');
2866         }
2867
2868         // not sure how we can show data in here..
2869         //if (this.tmpl) {
2870         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2871         //}
2872
2873         Roo.get(document.body).addClass("x-body-masked");
2874         
2875         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2876         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2877         this.maskEl.addClass('show');
2878         
2879         this.resize();
2880         
2881         this.fireEvent('show', this);
2882
2883         // set zindex here - otherwise it appears to be ignored...
2884         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2885
2886         (function () {
2887             this.items.forEach( function(e) {
2888                 e.layout ? e.layout() : false;
2889
2890             });
2891         }).defer(100,this);
2892
2893     },
2894     hide : function()
2895     {
2896         if(this.fireEvent("beforehide", this) !== false){
2897             this.maskEl.removeClass('show');
2898             Roo.get(document.body).removeClass("x-body-masked");
2899             this.el.removeClass('in');
2900             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2901
2902             if(this.animate){ // why
2903                 this.el.addClass('hideing');
2904                 (function(){
2905                     if (!this.el.hasClass('hideing')) {
2906                         return; // it's been shown again...
2907                     }
2908                     this.el.removeClass('show');
2909                     this.el.removeClass('hideing');
2910                 }).defer(150,this);
2911                 
2912             }else{
2913                  this.el.removeClass('show');
2914             }
2915             this.fireEvent('hide', this);
2916         }
2917     },
2918     isVisible : function()
2919     {
2920         
2921         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2922         
2923     },
2924
2925     addButton : function(str, cb)
2926     {
2927
2928
2929         var b = Roo.apply({}, { html : str } );
2930         b.xns = b.xns || Roo.bootstrap;
2931         b.xtype = b.xtype || 'Button';
2932         if (typeof(b.listeners) == 'undefined') {
2933             b.listeners = { click : cb.createDelegate(this)  };
2934         }
2935
2936         var btn = Roo.factory(b);
2937
2938         btn.render(this.el.select('.modal-footer div').first());
2939
2940         return btn;
2941
2942     },
2943
2944     setDefaultButton : function(btn)
2945     {
2946         //this.el.select('.modal-footer').()
2947     },
2948     diff : false,
2949
2950     resizeTo: function(w,h)
2951     {
2952         // skip.. ?? why??
2953
2954         this.dialogEl.setWidth(w);
2955         if (this.diff === false) {
2956             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2957         }
2958
2959         this.bodyEl.setHeight(h-this.diff);
2960
2961         this.fireEvent('resize', this);
2962
2963     },
2964     setContentSize  : function(w, h)
2965     {
2966
2967     },
2968     onButtonClick: function(btn,e)
2969     {
2970         //Roo.log([a,b,c]);
2971         this.fireEvent('btnclick', btn.name, e);
2972     },
2973      /**
2974      * Set the title of the Dialog
2975      * @param {String} str new Title
2976      */
2977     setTitle: function(str) {
2978         this.titleEl.dom.innerHTML = str;
2979     },
2980     /**
2981      * Set the body of the Dialog
2982      * @param {String} str new Title
2983      */
2984     setBody: function(str) {
2985         this.bodyEl.dom.innerHTML = str;
2986     },
2987     /**
2988      * Set the body of the Dialog using the template
2989      * @param {Obj} data - apply this data to the template and replace the body contents.
2990      */
2991     applyBody: function(obj)
2992     {
2993         if (!this.tmpl) {
2994             Roo.log("Error - using apply Body without a template");
2995             //code
2996         }
2997         this.tmpl.overwrite(this.bodyEl, obj);
2998     }
2999
3000 });
3001
3002
3003 Roo.apply(Roo.bootstrap.Modal,  {
3004     /**
3005          * Button config that displays a single OK button
3006          * @type Object
3007          */
3008         OK :  [{
3009             name : 'ok',
3010             weight : 'primary',
3011             html : 'OK'
3012         }],
3013         /**
3014          * Button config that displays Yes and No buttons
3015          * @type Object
3016          */
3017         YESNO : [
3018             {
3019                 name  : 'no',
3020                 html : 'No'
3021             },
3022             {
3023                 name  :'yes',
3024                 weight : 'primary',
3025                 html : 'Yes'
3026             }
3027         ],
3028
3029         /**
3030          * Button config that displays OK and Cancel buttons
3031          * @type Object
3032          */
3033         OKCANCEL : [
3034             {
3035                name : 'cancel',
3036                 html : 'Cancel'
3037             },
3038             {
3039                 name : 'ok',
3040                 weight : 'primary',
3041                 html : 'OK'
3042             }
3043         ],
3044         /**
3045          * Button config that displays Yes, No and Cancel buttons
3046          * @type Object
3047          */
3048         YESNOCANCEL : [
3049             {
3050                 name : 'yes',
3051                 weight : 'primary',
3052                 html : 'Yes'
3053             },
3054             {
3055                 name : 'no',
3056                 html : 'No'
3057             },
3058             {
3059                 name : 'cancel',
3060                 html : 'Cancel'
3061             }
3062         ],
3063         
3064         zIndex : 10001
3065 });
3066 /*
3067  * - LGPL
3068  *
3069  * messagebox - can be used as a replace
3070  * 
3071  */
3072 /**
3073  * @class Roo.MessageBox
3074  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3075  * Example usage:
3076  *<pre><code>
3077 // Basic alert:
3078 Roo.Msg.alert('Status', 'Changes saved successfully.');
3079
3080 // Prompt for user data:
3081 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3082     if (btn == 'ok'){
3083         // process text value...
3084     }
3085 });
3086
3087 // Show a dialog using config options:
3088 Roo.Msg.show({
3089    title:'Save Changes?',
3090    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3091    buttons: Roo.Msg.YESNOCANCEL,
3092    fn: processResult,
3093    animEl: 'elId'
3094 });
3095 </code></pre>
3096  * @singleton
3097  */
3098 Roo.bootstrap.MessageBox = function(){
3099     var dlg, opt, mask, waitTimer;
3100     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3101     var buttons, activeTextEl, bwidth;
3102
3103     
3104     // private
3105     var handleButton = function(button){
3106         dlg.hide();
3107         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3108     };
3109
3110     // private
3111     var handleHide = function(){
3112         if(opt && opt.cls){
3113             dlg.el.removeClass(opt.cls);
3114         }
3115         //if(waitTimer){
3116         //    Roo.TaskMgr.stop(waitTimer);
3117         //    waitTimer = null;
3118         //}
3119     };
3120
3121     // private
3122     var updateButtons = function(b){
3123         var width = 0;
3124         if(!b){
3125             buttons["ok"].hide();
3126             buttons["cancel"].hide();
3127             buttons["yes"].hide();
3128             buttons["no"].hide();
3129             //dlg.footer.dom.style.display = 'none';
3130             return width;
3131         }
3132         dlg.footerEl.dom.style.display = '';
3133         for(var k in buttons){
3134             if(typeof buttons[k] != "function"){
3135                 if(b[k]){
3136                     buttons[k].show();
3137                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3138                     width += buttons[k].el.getWidth()+15;
3139                 }else{
3140                     buttons[k].hide();
3141                 }
3142             }
3143         }
3144         return width;
3145     };
3146
3147     // private
3148     var handleEsc = function(d, k, e){
3149         if(opt && opt.closable !== false){
3150             dlg.hide();
3151         }
3152         if(e){
3153             e.stopEvent();
3154         }
3155     };
3156
3157     return {
3158         /**
3159          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3160          * @return {Roo.BasicDialog} The BasicDialog element
3161          */
3162         getDialog : function(){
3163            if(!dlg){
3164                 dlg = new Roo.bootstrap.Modal( {
3165                     //draggable: true,
3166                     //resizable:false,
3167                     //constraintoviewport:false,
3168                     //fixedcenter:true,
3169                     //collapsible : false,
3170                     //shim:true,
3171                     //modal: true,
3172                 //    width: 'auto',
3173                   //  height:100,
3174                     //buttonAlign:"center",
3175                     closeClick : function(){
3176                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3177                             handleButton("no");
3178                         }else{
3179                             handleButton("cancel");
3180                         }
3181                     }
3182                 });
3183                 dlg.render();
3184                 dlg.on("hide", handleHide);
3185                 mask = dlg.mask;
3186                 //dlg.addKeyListener(27, handleEsc);
3187                 buttons = {};
3188                 this.buttons = buttons;
3189                 var bt = this.buttonText;
3190                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3191                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3192                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3193                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3194                 //Roo.log(buttons);
3195                 bodyEl = dlg.bodyEl.createChild({
3196
3197                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3198                         '<textarea class="roo-mb-textarea"></textarea>' +
3199                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3200                 });
3201                 msgEl = bodyEl.dom.firstChild;
3202                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3203                 textboxEl.enableDisplayMode();
3204                 textboxEl.addKeyListener([10,13], function(){
3205                     if(dlg.isVisible() && opt && opt.buttons){
3206                         if(opt.buttons.ok){
3207                             handleButton("ok");
3208                         }else if(opt.buttons.yes){
3209                             handleButton("yes");
3210                         }
3211                     }
3212                 });
3213                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3214                 textareaEl.enableDisplayMode();
3215                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3216                 progressEl.enableDisplayMode();
3217                 
3218                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3219                 var pf = progressEl.dom.firstChild;
3220                 if (pf) {
3221                     pp = Roo.get(pf.firstChild);
3222                     pp.setHeight(pf.offsetHeight);
3223                 }
3224                 
3225             }
3226             return dlg;
3227         },
3228
3229         /**
3230          * Updates the message box body text
3231          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3232          * the XHTML-compliant non-breaking space character '&amp;#160;')
3233          * @return {Roo.MessageBox} This message box
3234          */
3235         updateText : function(text)
3236         {
3237             if(!dlg.isVisible() && !opt.width){
3238                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3239                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3240             }
3241             msgEl.innerHTML = text || '&#160;';
3242       
3243             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3244             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3245             var w = Math.max(
3246                     Math.min(opt.width || cw , this.maxWidth), 
3247                     Math.max(opt.minWidth || this.minWidth, bwidth)
3248             );
3249             if(opt.prompt){
3250                 activeTextEl.setWidth(w);
3251             }
3252             if(dlg.isVisible()){
3253                 dlg.fixedcenter = false;
3254             }
3255             // to big, make it scroll. = But as usual stupid IE does not support
3256             // !important..
3257             
3258             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3259                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3260                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3261             } else {
3262                 bodyEl.dom.style.height = '';
3263                 bodyEl.dom.style.overflowY = '';
3264             }
3265             if (cw > w) {
3266                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3267             } else {
3268                 bodyEl.dom.style.overflowX = '';
3269             }
3270             
3271             dlg.setContentSize(w, bodyEl.getHeight());
3272             if(dlg.isVisible()){
3273                 dlg.fixedcenter = true;
3274             }
3275             return this;
3276         },
3277
3278         /**
3279          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3280          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3281          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3282          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3283          * @return {Roo.MessageBox} This message box
3284          */
3285         updateProgress : function(value, text){
3286             if(text){
3287                 this.updateText(text);
3288             }
3289             
3290             if (pp) { // weird bug on my firefox - for some reason this is not defined
3291                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3292                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3293             }
3294             return this;
3295         },        
3296
3297         /**
3298          * Returns true if the message box is currently displayed
3299          * @return {Boolean} True if the message box is visible, else false
3300          */
3301         isVisible : function(){
3302             return dlg && dlg.isVisible();  
3303         },
3304
3305         /**
3306          * Hides the message box if it is displayed
3307          */
3308         hide : function(){
3309             if(this.isVisible()){
3310                 dlg.hide();
3311             }  
3312         },
3313
3314         /**
3315          * Displays a new message box, or reinitializes an existing message box, based on the config options
3316          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3317          * The following config object properties are supported:
3318          * <pre>
3319 Property    Type             Description
3320 ----------  ---------------  ------------------------------------------------------------------------------------
3321 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3322                                    closes (defaults to undefined)
3323 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3324                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3325 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3326                                    progress and wait dialogs will ignore this property and always hide the
3327                                    close button as they can only be closed programmatically.
3328 cls               String           A custom CSS class to apply to the message box element
3329 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3330                                    displayed (defaults to 75)
3331 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3332                                    function will be btn (the name of the button that was clicked, if applicable,
3333                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3334                                    Progress and wait dialogs will ignore this option since they do not respond to
3335                                    user actions and can only be closed programmatically, so any required function
3336                                    should be called by the same code after it closes the dialog.
3337 icon              String           A CSS class that provides a background image to be used as an icon for
3338                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3339 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3340 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3341 modal             Boolean          False to allow user interaction with the page while the message box is
3342                                    displayed (defaults to true)
3343 msg               String           A string that will replace the existing message box body text (defaults
3344                                    to the XHTML-compliant non-breaking space character '&#160;')
3345 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3346 progress          Boolean          True to display a progress bar (defaults to false)
3347 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3348 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3349 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3350 title             String           The title text
3351 value             String           The string value to set into the active textbox element if displayed
3352 wait              Boolean          True to display a progress bar (defaults to false)
3353 width             Number           The width of the dialog in pixels
3354 </pre>
3355          *
3356          * Example usage:
3357          * <pre><code>
3358 Roo.Msg.show({
3359    title: 'Address',
3360    msg: 'Please enter your address:',
3361    width: 300,
3362    buttons: Roo.MessageBox.OKCANCEL,
3363    multiline: true,
3364    fn: saveAddress,
3365    animEl: 'addAddressBtn'
3366 });
3367 </code></pre>
3368          * @param {Object} config Configuration options
3369          * @return {Roo.MessageBox} This message box
3370          */
3371         show : function(options)
3372         {
3373             
3374             // this causes nightmares if you show one dialog after another
3375             // especially on callbacks..
3376              
3377             if(this.isVisible()){
3378                 
3379                 this.hide();
3380                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3381                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3382                 Roo.log("New Dialog Message:" +  options.msg )
3383                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3384                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3385                 
3386             }
3387             var d = this.getDialog();
3388             opt = options;
3389             d.setTitle(opt.title || "&#160;");
3390             d.closeEl.setDisplayed(opt.closable !== false);
3391             activeTextEl = textboxEl;
3392             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3393             if(opt.prompt){
3394                 if(opt.multiline){
3395                     textboxEl.hide();
3396                     textareaEl.show();
3397                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3398                         opt.multiline : this.defaultTextHeight);
3399                     activeTextEl = textareaEl;
3400                 }else{
3401                     textboxEl.show();
3402                     textareaEl.hide();
3403                 }
3404             }else{
3405                 textboxEl.hide();
3406                 textareaEl.hide();
3407             }
3408             progressEl.setDisplayed(opt.progress === true);
3409             this.updateProgress(0);
3410             activeTextEl.dom.value = opt.value || "";
3411             if(opt.prompt){
3412                 dlg.setDefaultButton(activeTextEl);
3413             }else{
3414                 var bs = opt.buttons;
3415                 var db = null;
3416                 if(bs && bs.ok){
3417                     db = buttons["ok"];
3418                 }else if(bs && bs.yes){
3419                     db = buttons["yes"];
3420                 }
3421                 dlg.setDefaultButton(db);
3422             }
3423             bwidth = updateButtons(opt.buttons);
3424             this.updateText(opt.msg);
3425             if(opt.cls){
3426                 d.el.addClass(opt.cls);
3427             }
3428             d.proxyDrag = opt.proxyDrag === true;
3429             d.modal = opt.modal !== false;
3430             d.mask = opt.modal !== false ? mask : false;
3431             if(!d.isVisible()){
3432                 // force it to the end of the z-index stack so it gets a cursor in FF
3433                 document.body.appendChild(dlg.el.dom);
3434                 d.animateTarget = null;
3435                 d.show(options.animEl);
3436             }
3437             return this;
3438         },
3439
3440         /**
3441          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3442          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3443          * and closing the message box when the process is complete.
3444          * @param {String} title The title bar text
3445          * @param {String} msg The message box body text
3446          * @return {Roo.MessageBox} This message box
3447          */
3448         progress : function(title, msg){
3449             this.show({
3450                 title : title,
3451                 msg : msg,
3452                 buttons: false,
3453                 progress:true,
3454                 closable:false,
3455                 minWidth: this.minProgressWidth,
3456                 modal : true
3457             });
3458             return this;
3459         },
3460
3461         /**
3462          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3463          * If a callback function is passed it will be called after the user clicks the button, and the
3464          * id of the button that was clicked will be passed as the only parameter to the callback
3465          * (could also be the top-right close button).
3466          * @param {String} title The title bar text
3467          * @param {String} msg The message box body text
3468          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3469          * @param {Object} scope (optional) The scope of the callback function
3470          * @return {Roo.MessageBox} This message box
3471          */
3472         alert : function(title, msg, fn, scope)
3473         {
3474             this.show({
3475                 title : title,
3476                 msg : msg,
3477                 buttons: this.OK,
3478                 fn: fn,
3479                 closable : false,
3480                 scope : scope,
3481                 modal : true
3482             });
3483             return this;
3484         },
3485
3486         /**
3487          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3488          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3489          * You are responsible for closing the message box when the process is complete.
3490          * @param {String} msg The message box body text
3491          * @param {String} title (optional) The title bar text
3492          * @return {Roo.MessageBox} This message box
3493          */
3494         wait : function(msg, title){
3495             this.show({
3496                 title : title,
3497                 msg : msg,
3498                 buttons: false,
3499                 closable:false,
3500                 progress:true,
3501                 modal:true,
3502                 width:300,
3503                 wait:true
3504             });
3505             waitTimer = Roo.TaskMgr.start({
3506                 run: function(i){
3507                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3508                 },
3509                 interval: 1000
3510             });
3511             return this;
3512         },
3513
3514         /**
3515          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3516          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3517          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3518          * @param {String} title The title bar text
3519          * @param {String} msg The message box body text
3520          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3521          * @param {Object} scope (optional) The scope of the callback function
3522          * @return {Roo.MessageBox} This message box
3523          */
3524         confirm : function(title, msg, fn, scope){
3525             this.show({
3526                 title : title,
3527                 msg : msg,
3528                 buttons: this.YESNO,
3529                 fn: fn,
3530                 scope : scope,
3531                 modal : true
3532             });
3533             return this;
3534         },
3535
3536         /**
3537          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3538          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3539          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3540          * (could also be the top-right close button) and the text that was entered will be passed as the two
3541          * parameters to the callback.
3542          * @param {String} title The title bar text
3543          * @param {String} msg The message box body text
3544          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3545          * @param {Object} scope (optional) The scope of the callback function
3546          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3547          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3548          * @return {Roo.MessageBox} This message box
3549          */
3550         prompt : function(title, msg, fn, scope, multiline){
3551             this.show({
3552                 title : title,
3553                 msg : msg,
3554                 buttons: this.OKCANCEL,
3555                 fn: fn,
3556                 minWidth:250,
3557                 scope : scope,
3558                 prompt:true,
3559                 multiline: multiline,
3560                 modal : true
3561             });
3562             return this;
3563         },
3564
3565         /**
3566          * Button config that displays a single OK button
3567          * @type Object
3568          */
3569         OK : {ok:true},
3570         /**
3571          * Button config that displays Yes and No buttons
3572          * @type Object
3573          */
3574         YESNO : {yes:true, no:true},
3575         /**
3576          * Button config that displays OK and Cancel buttons
3577          * @type Object
3578          */
3579         OKCANCEL : {ok:true, cancel:true},
3580         /**
3581          * Button config that displays Yes, No and Cancel buttons
3582          * @type Object
3583          */
3584         YESNOCANCEL : {yes:true, no:true, cancel:true},
3585
3586         /**
3587          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3588          * @type Number
3589          */
3590         defaultTextHeight : 75,
3591         /**
3592          * The maximum width in pixels of the message box (defaults to 600)
3593          * @type Number
3594          */
3595         maxWidth : 600,
3596         /**
3597          * The minimum width in pixels of the message box (defaults to 100)
3598          * @type Number
3599          */
3600         minWidth : 100,
3601         /**
3602          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3603          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3604          * @type Number
3605          */
3606         minProgressWidth : 250,
3607         /**
3608          * An object containing the default button text strings that can be overriden for localized language support.
3609          * Supported properties are: ok, cancel, yes and no.
3610          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3611          * @type Object
3612          */
3613         buttonText : {
3614             ok : "OK",
3615             cancel : "Cancel",
3616             yes : "Yes",
3617             no : "No"
3618         }
3619     };
3620 }();
3621
3622 /**
3623  * Shorthand for {@link Roo.MessageBox}
3624  */
3625 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3626 Roo.Msg = Roo.Msg || Roo.MessageBox;
3627 /*
3628  * - LGPL
3629  *
3630  * navbar
3631  * 
3632  */
3633
3634 /**
3635  * @class Roo.bootstrap.Navbar
3636  * @extends Roo.bootstrap.Component
3637  * Bootstrap Navbar class
3638
3639  * @constructor
3640  * Create a new Navbar
3641  * @param {Object} config The config object
3642  */
3643
3644
3645 Roo.bootstrap.Navbar = function(config){
3646     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3647     this.addEvents({
3648         // raw events
3649         /**
3650          * @event beforetoggle
3651          * Fire before toggle the menu
3652          * @param {Roo.EventObject} e
3653          */
3654         "beforetoggle" : true
3655     });
3656 };
3657
3658 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3659     
3660     
3661    
3662     // private
3663     navItems : false,
3664     loadMask : false,
3665     
3666     
3667     getAutoCreate : function(){
3668         
3669         
3670         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3671         
3672     },
3673     
3674     initEvents :function ()
3675     {
3676         //Roo.log(this.el.select('.navbar-toggle',true));
3677         this.el.select('.navbar-toggle',true).on('click', function() {
3678             if(this.fireEvent('beforetoggle', this) !== false){
3679                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3680             }
3681             
3682         }, this);
3683         
3684         var mark = {
3685             tag: "div",
3686             cls:"x-dlg-mask"
3687         };
3688         
3689         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3690         
3691         var size = this.el.getSize();
3692         this.maskEl.setSize(size.width, size.height);
3693         this.maskEl.enableDisplayMode("block");
3694         this.maskEl.hide();
3695         
3696         if(this.loadMask){
3697             this.maskEl.show();
3698         }
3699     },
3700     
3701     
3702     getChildContainer : function()
3703     {
3704         if (this.el.select('.collapse').getCount()) {
3705             return this.el.select('.collapse',true).first();
3706         }
3707         
3708         return this.el;
3709     },
3710     
3711     mask : function()
3712     {
3713         this.maskEl.show();
3714     },
3715     
3716     unmask : function()
3717     {
3718         this.maskEl.hide();
3719     } 
3720     
3721     
3722     
3723     
3724 });
3725
3726
3727
3728  
3729
3730  /*
3731  * - LGPL
3732  *
3733  * navbar
3734  * 
3735  */
3736
3737 /**
3738  * @class Roo.bootstrap.NavSimplebar
3739  * @extends Roo.bootstrap.Navbar
3740  * Bootstrap Sidebar class
3741  *
3742  * @cfg {Boolean} inverse is inverted color
3743  * 
3744  * @cfg {String} type (nav | pills | tabs)
3745  * @cfg {Boolean} arrangement stacked | justified
3746  * @cfg {String} align (left | right) alignment
3747  * 
3748  * @cfg {Boolean} main (true|false) main nav bar? default false
3749  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3750  * 
3751  * @cfg {String} tag (header|footer|nav|div) default is nav 
3752
3753  * 
3754  * 
3755  * 
3756  * @constructor
3757  * Create a new Sidebar
3758  * @param {Object} config The config object
3759  */
3760
3761
3762 Roo.bootstrap.NavSimplebar = function(config){
3763     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3764 };
3765
3766 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3767     
3768     inverse: false,
3769     
3770     type: false,
3771     arrangement: '',
3772     align : false,
3773     
3774     
3775     
3776     main : false,
3777     
3778     
3779     tag : false,
3780     
3781     
3782     getAutoCreate : function(){
3783         
3784         
3785         var cfg = {
3786             tag : this.tag || 'div',
3787             cls : 'navbar'
3788         };
3789           
3790         
3791         cfg.cn = [
3792             {
3793                 cls: 'nav',
3794                 tag : 'ul'
3795             }
3796         ];
3797         
3798          
3799         this.type = this.type || 'nav';
3800         if (['tabs','pills'].indexOf(this.type)!==-1) {
3801             cfg.cn[0].cls += ' nav-' + this.type
3802         
3803         
3804         } else {
3805             if (this.type!=='nav') {
3806                 Roo.log('nav type must be nav/tabs/pills')
3807             }
3808             cfg.cn[0].cls += ' navbar-nav'
3809         }
3810         
3811         
3812         
3813         
3814         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3815             cfg.cn[0].cls += ' nav-' + this.arrangement;
3816         }
3817         
3818         
3819         if (this.align === 'right') {
3820             cfg.cn[0].cls += ' navbar-right';
3821         }
3822         
3823         if (this.inverse) {
3824             cfg.cls += ' navbar-inverse';
3825             
3826         }
3827         
3828         
3829         return cfg;
3830     
3831         
3832     }
3833     
3834     
3835     
3836 });
3837
3838
3839
3840  
3841
3842  
3843        /*
3844  * - LGPL
3845  *
3846  * navbar
3847  * 
3848  */
3849
3850 /**
3851  * @class Roo.bootstrap.NavHeaderbar
3852  * @extends Roo.bootstrap.NavSimplebar
3853  * Bootstrap Sidebar class
3854  *
3855  * @cfg {String} brand what is brand
3856  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3857  * @cfg {String} brand_href href of the brand
3858  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3859  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3860  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3861  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3862  * 
3863  * @constructor
3864  * Create a new Sidebar
3865  * @param {Object} config The config object
3866  */
3867
3868
3869 Roo.bootstrap.NavHeaderbar = function(config){
3870     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3871       
3872 };
3873
3874 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3875     
3876     position: '',
3877     brand: '',
3878     brand_href: false,
3879     srButton : true,
3880     autohide : false,
3881     desktopCenter : false,
3882    
3883     
3884     getAutoCreate : function(){
3885         
3886         var   cfg = {
3887             tag: this.nav || 'nav',
3888             cls: 'navbar',
3889             role: 'navigation',
3890             cn: []
3891         };
3892         
3893         var cn = cfg.cn;
3894         if (this.desktopCenter) {
3895             cn.push({cls : 'container', cn : []});
3896             cn = cn[0].cn;
3897         }
3898         
3899         if(this.srButton){
3900             cn.push({
3901                 tag: 'div',
3902                 cls: 'navbar-header',
3903                 cn: [
3904                     {
3905                         tag: 'button',
3906                         type: 'button',
3907                         cls: 'navbar-toggle',
3908                         'data-toggle': 'collapse',
3909                         cn: [
3910                             {
3911                                 tag: 'span',
3912                                 cls: 'sr-only',
3913                                 html: 'Toggle navigation'
3914                             },
3915                             {
3916                                 tag: 'span',
3917                                 cls: 'icon-bar'
3918                             },
3919                             {
3920                                 tag: 'span',
3921                                 cls: 'icon-bar'
3922                             },
3923                             {
3924                                 tag: 'span',
3925                                 cls: 'icon-bar'
3926                             }
3927                         ]
3928                     }
3929                 ]
3930             });
3931         }
3932         
3933         cn.push({
3934             tag: 'div',
3935             cls: 'collapse navbar-collapse',
3936             cn : []
3937         });
3938         
3939         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3940         
3941         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3942             cfg.cls += ' navbar-' + this.position;
3943             
3944             // tag can override this..
3945             
3946             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3947         }
3948         
3949         if (this.brand !== '') {
3950             cn[0].cn.push({
3951                 tag: 'a',
3952                 href: this.brand_href ? this.brand_href : '#',
3953                 cls: 'navbar-brand',
3954                 cn: [
3955                 this.brand
3956                 ]
3957             });
3958         }
3959         
3960         if(this.main){
3961             cfg.cls += ' main-nav';
3962         }
3963         
3964         
3965         return cfg;
3966
3967         
3968     },
3969     getHeaderChildContainer : function()
3970     {
3971         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3972             return this.el.select('.navbar-header',true).first();
3973         }
3974         
3975         return this.getChildContainer();
3976     },
3977     
3978     
3979     initEvents : function()
3980     {
3981         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3982         
3983         if (this.autohide) {
3984             
3985             var prevScroll = 0;
3986             var ft = this.el;
3987             
3988             Roo.get(document).on('scroll',function(e) {
3989                 var ns = Roo.get(document).getScroll().top;
3990                 var os = prevScroll;
3991                 prevScroll = ns;
3992                 
3993                 if(ns > os){
3994                     ft.removeClass('slideDown');
3995                     ft.addClass('slideUp');
3996                     return;
3997                 }
3998                 ft.removeClass('slideUp');
3999                 ft.addClass('slideDown');
4000                  
4001               
4002           },this);
4003         }
4004     }    
4005     
4006 });
4007
4008
4009
4010  
4011
4012  /*
4013  * - LGPL
4014  *
4015  * navbar
4016  * 
4017  */
4018
4019 /**
4020  * @class Roo.bootstrap.NavSidebar
4021  * @extends Roo.bootstrap.Navbar
4022  * Bootstrap Sidebar class
4023  * 
4024  * @constructor
4025  * Create a new Sidebar
4026  * @param {Object} config The config object
4027  */
4028
4029
4030 Roo.bootstrap.NavSidebar = function(config){
4031     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4032 };
4033
4034 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4035     
4036     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4037     
4038     getAutoCreate : function(){
4039         
4040         
4041         return  {
4042             tag: 'div',
4043             cls: 'sidebar sidebar-nav'
4044         };
4045     
4046         
4047     }
4048     
4049     
4050     
4051 });
4052
4053
4054
4055  
4056
4057  /*
4058  * - LGPL
4059  *
4060  * nav group
4061  * 
4062  */
4063
4064 /**
4065  * @class Roo.bootstrap.NavGroup
4066  * @extends Roo.bootstrap.Component
4067  * Bootstrap NavGroup class
4068  * @cfg {String} align (left|right)
4069  * @cfg {Boolean} inverse
4070  * @cfg {String} type (nav|pills|tab) default nav
4071  * @cfg {String} navId - reference Id for navbar.
4072
4073  * 
4074  * @constructor
4075  * Create a new nav group
4076  * @param {Object} config The config object
4077  */
4078
4079 Roo.bootstrap.NavGroup = function(config){
4080     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4081     this.navItems = [];
4082    
4083     Roo.bootstrap.NavGroup.register(this);
4084      this.addEvents({
4085         /**
4086              * @event changed
4087              * Fires when the active item changes
4088              * @param {Roo.bootstrap.NavGroup} this
4089              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4090              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4091          */
4092         'changed': true
4093      });
4094     
4095 };
4096
4097 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4098     
4099     align: '',
4100     inverse: false,
4101     form: false,
4102     type: 'nav',
4103     navId : '',
4104     // private
4105     
4106     navItems : false, 
4107     
4108     getAutoCreate : function()
4109     {
4110         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4111         
4112         cfg = {
4113             tag : 'ul',
4114             cls: 'nav' 
4115         };
4116         
4117         if (['tabs','pills'].indexOf(this.type)!==-1) {
4118             cfg.cls += ' nav-' + this.type
4119         } else {
4120             if (this.type!=='nav') {
4121                 Roo.log('nav type must be nav/tabs/pills')
4122             }
4123             cfg.cls += ' navbar-nav'
4124         }
4125         
4126         if (this.parent() && this.parent().sidebar) {
4127             cfg = {
4128                 tag: 'ul',
4129                 cls: 'dashboard-menu sidebar-menu'
4130             };
4131             
4132             return cfg;
4133         }
4134         
4135         if (this.form === true) {
4136             cfg = {
4137                 tag: 'form',
4138                 cls: 'navbar-form'
4139             };
4140             
4141             if (this.align === 'right') {
4142                 cfg.cls += ' navbar-right';
4143             } else {
4144                 cfg.cls += ' navbar-left';
4145             }
4146         }
4147         
4148         if (this.align === 'right') {
4149             cfg.cls += ' navbar-right';
4150         }
4151         
4152         if (this.inverse) {
4153             cfg.cls += ' navbar-inverse';
4154             
4155         }
4156         
4157         
4158         return cfg;
4159     },
4160     /**
4161     * sets the active Navigation item
4162     * @param {Roo.bootstrap.NavItem} the new current navitem
4163     */
4164     setActiveItem : function(item)
4165     {
4166         var prev = false;
4167         Roo.each(this.navItems, function(v){
4168             if (v == item) {
4169                 return ;
4170             }
4171             if (v.isActive()) {
4172                 v.setActive(false, true);
4173                 prev = v;
4174                 
4175             }
4176             
4177         });
4178
4179         item.setActive(true, true);
4180         this.fireEvent('changed', this, item, prev);
4181         
4182         
4183     },
4184     /**
4185     * gets the active Navigation item
4186     * @return {Roo.bootstrap.NavItem} the current navitem
4187     */
4188     getActive : function()
4189     {
4190         
4191         var prev = false;
4192         Roo.each(this.navItems, function(v){
4193             
4194             if (v.isActive()) {
4195                 prev = v;
4196                 
4197             }
4198             
4199         });
4200         return prev;
4201     },
4202     
4203     indexOfNav : function()
4204     {
4205         
4206         var prev = false;
4207         Roo.each(this.navItems, function(v,i){
4208             
4209             if (v.isActive()) {
4210                 prev = i;
4211                 
4212             }
4213             
4214         });
4215         return prev;
4216     },
4217     /**
4218     * adds a Navigation item
4219     * @param {Roo.bootstrap.NavItem} the navitem to add
4220     */
4221     addItem : function(cfg)
4222     {
4223         var cn = new Roo.bootstrap.NavItem(cfg);
4224         this.register(cn);
4225         cn.parentId = this.id;
4226         cn.onRender(this.el, null);
4227         return cn;
4228     },
4229     /**
4230     * register a Navigation item
4231     * @param {Roo.bootstrap.NavItem} the navitem to add
4232     */
4233     register : function(item)
4234     {
4235         this.navItems.push( item);
4236         item.navId = this.navId;
4237     
4238     },
4239     
4240     /**
4241     * clear all the Navigation item
4242     */
4243    
4244     clearAll : function()
4245     {
4246         this.navItems = [];
4247         this.el.dom.innerHTML = '';
4248     },
4249     
4250     getNavItem: function(tabId)
4251     {
4252         var ret = false;
4253         Roo.each(this.navItems, function(e) {
4254             if (e.tabId == tabId) {
4255                ret =  e;
4256                return false;
4257             }
4258             return true;
4259             
4260         });
4261         return ret;
4262     },
4263     
4264     setActiveNext : function()
4265     {
4266         var i = this.indexOfNav(this.getActive());
4267         if (i > this.navItems.length) {
4268             return;
4269         }
4270         this.setActiveItem(this.navItems[i+1]);
4271     },
4272     setActivePrev : function()
4273     {
4274         var i = this.indexOfNav(this.getActive());
4275         if (i  < 1) {
4276             return;
4277         }
4278         this.setActiveItem(this.navItems[i-1]);
4279     },
4280     clearWasActive : function(except) {
4281         Roo.each(this.navItems, function(e) {
4282             if (e.tabId != except.tabId && e.was_active) {
4283                e.was_active = false;
4284                return false;
4285             }
4286             return true;
4287             
4288         });
4289     },
4290     getWasActive : function ()
4291     {
4292         var r = false;
4293         Roo.each(this.navItems, function(e) {
4294             if (e.was_active) {
4295                r = e;
4296                return false;
4297             }
4298             return true;
4299             
4300         });
4301         return r;
4302     }
4303     
4304     
4305 });
4306
4307  
4308 Roo.apply(Roo.bootstrap.NavGroup, {
4309     
4310     groups: {},
4311      /**
4312     * register a Navigation Group
4313     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4314     */
4315     register : function(navgrp)
4316     {
4317         this.groups[navgrp.navId] = navgrp;
4318         
4319     },
4320     /**
4321     * fetch a Navigation Group based on the navigation ID
4322     * @param {string} the navgroup to add
4323     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4324     */
4325     get: function(navId) {
4326         if (typeof(this.groups[navId]) == 'undefined') {
4327             return false;
4328             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4329         }
4330         return this.groups[navId] ;
4331     }
4332     
4333     
4334     
4335 });
4336
4337  /*
4338  * - LGPL
4339  *
4340  * row
4341  * 
4342  */
4343
4344 /**
4345  * @class Roo.bootstrap.NavItem
4346  * @extends Roo.bootstrap.Component
4347  * Bootstrap Navbar.NavItem class
4348  * @cfg {String} href  link to
4349  * @cfg {String} html content of button
4350  * @cfg {String} badge text inside badge
4351  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4352  * @cfg {String} glyphicon name of glyphicon
4353  * @cfg {String} icon name of font awesome icon
4354  * @cfg {Boolean} active Is item active
4355  * @cfg {Boolean} disabled Is item disabled
4356  
4357  * @cfg {Boolean} preventDefault (true | false) default false
4358  * @cfg {String} tabId the tab that this item activates.
4359  * @cfg {String} tagtype (a|span) render as a href or span?
4360  * @cfg {Boolean} animateRef (true|false) link to element default false  
4361   
4362  * @constructor
4363  * Create a new Navbar Item
4364  * @param {Object} config The config object
4365  */
4366 Roo.bootstrap.NavItem = function(config){
4367     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4368     this.addEvents({
4369         // raw events
4370         /**
4371          * @event click
4372          * The raw click event for the entire grid.
4373          * @param {Roo.EventObject} e
4374          */
4375         "click" : true,
4376          /**
4377             * @event changed
4378             * Fires when the active item active state changes
4379             * @param {Roo.bootstrap.NavItem} this
4380             * @param {boolean} state the new state
4381              
4382          */
4383         'changed': true,
4384         /**
4385             * @event scrollto
4386             * Fires when scroll to element
4387             * @param {Roo.bootstrap.NavItem} this
4388             * @param {Object} options
4389             * @param {Roo.EventObject} e
4390              
4391          */
4392         'scrollto': true
4393     });
4394    
4395 };
4396
4397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4398     
4399     href: false,
4400     html: '',
4401     badge: '',
4402     icon: false,
4403     glyphicon: false,
4404     active: false,
4405     preventDefault : false,
4406     tabId : false,
4407     tagtype : 'a',
4408     disabled : false,
4409     animateRef : false,
4410     was_active : false,
4411     
4412     getAutoCreate : function(){
4413          
4414         var cfg = {
4415             tag: 'li',
4416             cls: 'nav-item'
4417             
4418         };
4419         
4420         if (this.active) {
4421             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4422         }
4423         if (this.disabled) {
4424             cfg.cls += ' disabled';
4425         }
4426         
4427         if (this.href || this.html || this.glyphicon || this.icon) {
4428             cfg.cn = [
4429                 {
4430                     tag: this.tagtype,
4431                     href : this.href || "#",
4432                     html: this.html || ''
4433                 }
4434             ];
4435             
4436             if (this.icon) {
4437                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4438             }
4439
4440             if(this.glyphicon) {
4441                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4442             }
4443             
4444             if (this.menu) {
4445                 
4446                 cfg.cn[0].html += " <span class='caret'></span>";
4447              
4448             }
4449             
4450             if (this.badge !== '') {
4451                  
4452                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4453             }
4454         }
4455         
4456         
4457         
4458         return cfg;
4459     },
4460     initEvents: function() 
4461     {
4462         if (typeof (this.menu) != 'undefined') {
4463             this.menu.parentType = this.xtype;
4464             this.menu.triggerEl = this.el;
4465             this.menu = this.addxtype(Roo.apply({}, this.menu));
4466         }
4467         
4468         this.el.select('a',true).on('click', this.onClick, this);
4469         
4470         if(this.tagtype == 'span'){
4471             this.el.select('span',true).on('click', this.onClick, this);
4472         }
4473        
4474         // at this point parent should be available..
4475         this.parent().register(this);
4476     },
4477     
4478     onClick : function(e)
4479     {
4480         if (e.getTarget('.dropdown-menu-item')) {
4481             // did you click on a menu itemm.... - then don't trigger onclick..
4482             return;
4483         }
4484         
4485         if(
4486                 this.preventDefault || 
4487                 this.href == '#' 
4488         ){
4489             Roo.log("NavItem - prevent Default?");
4490             e.preventDefault();
4491         }
4492         
4493         if (this.disabled) {
4494             return;
4495         }
4496         
4497         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4498         if (tg && tg.transition) {
4499             Roo.log("waiting for the transitionend");
4500             return;
4501         }
4502         
4503         
4504         
4505         //Roo.log("fire event clicked");
4506         if(this.fireEvent('click', this, e) === false){
4507             return;
4508         };
4509         
4510         if(this.tagtype == 'span'){
4511             return;
4512         }
4513         
4514         //Roo.log(this.href);
4515         var ael = this.el.select('a',true).first();
4516         //Roo.log(ael);
4517         
4518         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4519             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4520             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4521                 return; // ignore... - it's a 'hash' to another page.
4522             }
4523             Roo.log("NavItem - prevent Default?");
4524             e.preventDefault();
4525             this.scrollToElement(e);
4526         }
4527         
4528         
4529         var p =  this.parent();
4530    
4531         if (['tabs','pills'].indexOf(p.type)!==-1) {
4532             if (typeof(p.setActiveItem) !== 'undefined') {
4533                 p.setActiveItem(this);
4534             }
4535         }
4536         
4537         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4538         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4539             // remove the collapsed menu expand...
4540             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4541         }
4542     },
4543     
4544     isActive: function () {
4545         return this.active
4546     },
4547     setActive : function(state, fire, is_was_active)
4548     {
4549         if (this.active && !state && this.navId) {
4550             this.was_active = true;
4551             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4552             if (nv) {
4553                 nv.clearWasActive(this);
4554             }
4555             
4556         }
4557         this.active = state;
4558         
4559         if (!state ) {
4560             this.el.removeClass('active');
4561         } else if (!this.el.hasClass('active')) {
4562             this.el.addClass('active');
4563         }
4564         if (fire) {
4565             this.fireEvent('changed', this, state);
4566         }
4567         
4568         // show a panel if it's registered and related..
4569         
4570         if (!this.navId || !this.tabId || !state || is_was_active) {
4571             return;
4572         }
4573         
4574         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4575         if (!tg) {
4576             return;
4577         }
4578         var pan = tg.getPanelByName(this.tabId);
4579         if (!pan) {
4580             return;
4581         }
4582         // if we can not flip to new panel - go back to old nav highlight..
4583         if (false == tg.showPanel(pan)) {
4584             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4585             if (nv) {
4586                 var onav = nv.getWasActive();
4587                 if (onav) {
4588                     onav.setActive(true, false, true);
4589                 }
4590             }
4591             
4592         }
4593         
4594         
4595         
4596     },
4597      // this should not be here...
4598     setDisabled : function(state)
4599     {
4600         this.disabled = state;
4601         if (!state ) {
4602             this.el.removeClass('disabled');
4603         } else if (!this.el.hasClass('disabled')) {
4604             this.el.addClass('disabled');
4605         }
4606         
4607     },
4608     
4609     /**
4610      * Fetch the element to display the tooltip on.
4611      * @return {Roo.Element} defaults to this.el
4612      */
4613     tooltipEl : function()
4614     {
4615         return this.el.select('' + this.tagtype + '', true).first();
4616     },
4617     
4618     scrollToElement : function(e)
4619     {
4620         var c = document.body;
4621         
4622         /*
4623          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4624          */
4625         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4626             c = document.documentElement;
4627         }
4628         
4629         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4630         
4631         if(!target){
4632             return;
4633         }
4634
4635         var o = target.calcOffsetsTo(c);
4636         
4637         var options = {
4638             target : target,
4639             value : o[1]
4640         };
4641         
4642         this.fireEvent('scrollto', this, options, e);
4643         
4644         Roo.get(c).scrollTo('top', options.value, true);
4645         
4646         return;
4647     }
4648 });
4649  
4650
4651  /*
4652  * - LGPL
4653  *
4654  * sidebar item
4655  *
4656  *  li
4657  *    <span> icon </span>
4658  *    <span> text </span>
4659  *    <span>badge </span>
4660  */
4661
4662 /**
4663  * @class Roo.bootstrap.NavSidebarItem
4664  * @extends Roo.bootstrap.NavItem
4665  * Bootstrap Navbar.NavSidebarItem class
4666  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4667  * {Boolean} open is the menu open
4668  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4669  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4670  * {String} buttonSize (sm|md|lg)the extra classes for the button
4671  * {Boolean} showArrow show arrow next to the text (default true)
4672  * @constructor
4673  * Create a new Navbar Button
4674  * @param {Object} config The config object
4675  */
4676 Roo.bootstrap.NavSidebarItem = function(config){
4677     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4678     this.addEvents({
4679         // raw events
4680         /**
4681          * @event click
4682          * The raw click event for the entire grid.
4683          * @param {Roo.EventObject} e
4684          */
4685         "click" : true,
4686          /**
4687             * @event changed
4688             * Fires when the active item active state changes
4689             * @param {Roo.bootstrap.NavSidebarItem} this
4690             * @param {boolean} state the new state
4691              
4692          */
4693         'changed': true
4694     });
4695    
4696 };
4697
4698 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4699     
4700     badgeWeight : 'default',
4701     
4702     open: false,
4703     
4704     buttonView : false,
4705     
4706     buttonWeight : 'default',
4707     
4708     buttonSize : 'md',
4709     
4710     showArrow : true,
4711     
4712     getAutoCreate : function(){
4713         
4714         
4715         var a = {
4716                 tag: 'a',
4717                 href : this.href || '#',
4718                 cls: '',
4719                 html : '',
4720                 cn : []
4721         };
4722         
4723         if(this.buttonView){
4724             a = {
4725                 tag: 'button',
4726                 href : this.href || '#',
4727                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4728                 html : this.html,
4729                 cn : []
4730             };
4731         }
4732         
4733         var cfg = {
4734             tag: 'li',
4735             cls: '',
4736             cn: [ a ]
4737         };
4738         
4739         if (this.active) {
4740             cfg.cls += ' active';
4741         }
4742         
4743         if (this.disabled) {
4744             cfg.cls += ' disabled';
4745         }
4746         if (this.open) {
4747             cfg.cls += ' open x-open';
4748         }
4749         // left icon..
4750         if (this.glyphicon || this.icon) {
4751             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4752             a.cn.push({ tag : 'i', cls : c }) ;
4753         }
4754         
4755         if(!this.buttonView){
4756             var span = {
4757                 tag: 'span',
4758                 html : this.html || ''
4759             };
4760
4761             a.cn.push(span);
4762             
4763         }
4764         
4765         if (this.badge !== '') {
4766             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4767         }
4768         
4769         if (this.menu) {
4770             
4771             if(this.showArrow){
4772                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4773             }
4774             
4775             a.cls += ' dropdown-toggle treeview' ;
4776         }
4777         
4778         return cfg;
4779     },
4780     
4781     initEvents : function()
4782     { 
4783         if (typeof (this.menu) != 'undefined') {
4784             this.menu.parentType = this.xtype;
4785             this.menu.triggerEl = this.el;
4786             this.menu = this.addxtype(Roo.apply({}, this.menu));
4787         }
4788         
4789         this.el.on('click', this.onClick, this);
4790         
4791         if(this.badge !== ''){
4792             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4793         }
4794         
4795     },
4796     
4797     onClick : function(e)
4798     {
4799         if(this.disabled){
4800             e.preventDefault();
4801             return;
4802         }
4803         
4804         if(this.preventDefault){
4805             e.preventDefault();
4806         }
4807         
4808         this.fireEvent('click', this);
4809     },
4810     
4811     disable : function()
4812     {
4813         this.setDisabled(true);
4814     },
4815     
4816     enable : function()
4817     {
4818         this.setDisabled(false);
4819     },
4820     
4821     setDisabled : function(state)
4822     {
4823         if(this.disabled == state){
4824             return;
4825         }
4826         
4827         this.disabled = state;
4828         
4829         if (state) {
4830             this.el.addClass('disabled');
4831             return;
4832         }
4833         
4834         this.el.removeClass('disabled');
4835         
4836         return;
4837     },
4838     
4839     setActive : function(state)
4840     {
4841         if(this.active == state){
4842             return;
4843         }
4844         
4845         this.active = state;
4846         
4847         if (state) {
4848             this.el.addClass('active');
4849             return;
4850         }
4851         
4852         this.el.removeClass('active');
4853         
4854         return;
4855     },
4856     
4857     isActive: function () 
4858     {
4859         return this.active;
4860     },
4861     
4862     setBadge : function(str)
4863     {
4864         if(!this.badgeEl){
4865             return;
4866         }
4867         
4868         this.badgeEl.dom.innerHTML = str;
4869     }
4870     
4871    
4872      
4873  
4874 });
4875  
4876
4877  /*
4878  * - LGPL
4879  *
4880  * row
4881  * 
4882  */
4883
4884 /**
4885  * @class Roo.bootstrap.Row
4886  * @extends Roo.bootstrap.Component
4887  * Bootstrap Row class (contains columns...)
4888  * 
4889  * @constructor
4890  * Create a new Row
4891  * @param {Object} config The config object
4892  */
4893
4894 Roo.bootstrap.Row = function(config){
4895     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4896 };
4897
4898 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4899     
4900     getAutoCreate : function(){
4901        return {
4902             cls: 'row clearfix'
4903        };
4904     }
4905     
4906     
4907 });
4908
4909  
4910
4911  /*
4912  * - LGPL
4913  *
4914  * element
4915  * 
4916  */
4917
4918 /**
4919  * @class Roo.bootstrap.Element
4920  * @extends Roo.bootstrap.Component
4921  * Bootstrap Element class
4922  * @cfg {String} html contents of the element
4923  * @cfg {String} tag tag of the element
4924  * @cfg {String} cls class of the element
4925  * @cfg {Boolean} preventDefault (true|false) default false
4926  * @cfg {Boolean} clickable (true|false) default false
4927  * 
4928  * @constructor
4929  * Create a new Element
4930  * @param {Object} config The config object
4931  */
4932
4933 Roo.bootstrap.Element = function(config){
4934     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4935     
4936     this.addEvents({
4937         // raw events
4938         /**
4939          * @event click
4940          * When a element is chick
4941          * @param {Roo.bootstrap.Element} this
4942          * @param {Roo.EventObject} e
4943          */
4944         "click" : true
4945     });
4946 };
4947
4948 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4949     
4950     tag: 'div',
4951     cls: '',
4952     html: '',
4953     preventDefault: false, 
4954     clickable: false,
4955     
4956     getAutoCreate : function(){
4957         
4958         var cfg = {
4959             tag: this.tag,
4960             // cls: this.cls, double assign in parent class Component.js :: onRender
4961             html: this.html
4962         };
4963         
4964         return cfg;
4965     },
4966     
4967     initEvents: function() 
4968     {
4969         Roo.bootstrap.Element.superclass.initEvents.call(this);
4970         
4971         if(this.clickable){
4972             this.el.on('click', this.onClick, this);
4973         }
4974         
4975     },
4976     
4977     onClick : function(e)
4978     {
4979         if(this.preventDefault){
4980             e.preventDefault();
4981         }
4982         
4983         this.fireEvent('click', this, e);
4984     },
4985     
4986     getValue : function()
4987     {
4988         return this.el.dom.innerHTML;
4989     },
4990     
4991     setValue : function(value)
4992     {
4993         this.el.dom.innerHTML = value;
4994     }
4995    
4996 });
4997
4998  
4999
5000  /*
5001  * - LGPL
5002  *
5003  * pagination
5004  * 
5005  */
5006
5007 /**
5008  * @class Roo.bootstrap.Pagination
5009  * @extends Roo.bootstrap.Component
5010  * Bootstrap Pagination class
5011  * @cfg {String} size xs | sm | md | lg
5012  * @cfg {Boolean} inverse false | true
5013  * 
5014  * @constructor
5015  * Create a new Pagination
5016  * @param {Object} config The config object
5017  */
5018
5019 Roo.bootstrap.Pagination = function(config){
5020     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5021 };
5022
5023 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5024     
5025     cls: false,
5026     size: false,
5027     inverse: false,
5028     
5029     getAutoCreate : function(){
5030         var cfg = {
5031             tag: 'ul',
5032                 cls: 'pagination'
5033         };
5034         if (this.inverse) {
5035             cfg.cls += ' inverse';
5036         }
5037         if (this.html) {
5038             cfg.html=this.html;
5039         }
5040         if (this.cls) {
5041             cfg.cls += " " + this.cls;
5042         }
5043         return cfg;
5044     }
5045    
5046 });
5047
5048  
5049
5050  /*
5051  * - LGPL
5052  *
5053  * Pagination item
5054  * 
5055  */
5056
5057
5058 /**
5059  * @class Roo.bootstrap.PaginationItem
5060  * @extends Roo.bootstrap.Component
5061  * Bootstrap PaginationItem class
5062  * @cfg {String} html text
5063  * @cfg {String} href the link
5064  * @cfg {Boolean} preventDefault (true | false) default true
5065  * @cfg {Boolean} active (true | false) default false
5066  * @cfg {Boolean} disabled default false
5067  * 
5068  * 
5069  * @constructor
5070  * Create a new PaginationItem
5071  * @param {Object} config The config object
5072  */
5073
5074
5075 Roo.bootstrap.PaginationItem = function(config){
5076     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5077     this.addEvents({
5078         // raw events
5079         /**
5080          * @event click
5081          * The raw click event for the entire grid.
5082          * @param {Roo.EventObject} e
5083          */
5084         "click" : true
5085     });
5086 };
5087
5088 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5089     
5090     href : false,
5091     html : false,
5092     preventDefault: true,
5093     active : false,
5094     cls : false,
5095     disabled: false,
5096     
5097     getAutoCreate : function(){
5098         var cfg= {
5099             tag: 'li',
5100             cn: [
5101                 {
5102                     tag : 'a',
5103                     href : this.href ? this.href : '#',
5104                     html : this.html ? this.html : ''
5105                 }
5106             ]
5107         };
5108         
5109         if(this.cls){
5110             cfg.cls = this.cls;
5111         }
5112         
5113         if(this.disabled){
5114             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5115         }
5116         
5117         if(this.active){
5118             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5119         }
5120         
5121         return cfg;
5122     },
5123     
5124     initEvents: function() {
5125         
5126         this.el.on('click', this.onClick, this);
5127         
5128     },
5129     onClick : function(e)
5130     {
5131         Roo.log('PaginationItem on click ');
5132         if(this.preventDefault){
5133             e.preventDefault();
5134         }
5135         
5136         if(this.disabled){
5137             return;
5138         }
5139         
5140         this.fireEvent('click', this, e);
5141     }
5142    
5143 });
5144
5145  
5146
5147  /*
5148  * - LGPL
5149  *
5150  * slider
5151  * 
5152  */
5153
5154
5155 /**
5156  * @class Roo.bootstrap.Slider
5157  * @extends Roo.bootstrap.Component
5158  * Bootstrap Slider class
5159  *    
5160  * @constructor
5161  * Create a new Slider
5162  * @param {Object} config The config object
5163  */
5164
5165 Roo.bootstrap.Slider = function(config){
5166     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5167 };
5168
5169 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5170     
5171     getAutoCreate : function(){
5172         
5173         var cfg = {
5174             tag: 'div',
5175             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5176             cn: [
5177                 {
5178                     tag: 'a',
5179                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5180                 }
5181             ]
5182         };
5183         
5184         return cfg;
5185     }
5186    
5187 });
5188
5189  /*
5190  * Based on:
5191  * Ext JS Library 1.1.1
5192  * Copyright(c) 2006-2007, Ext JS, LLC.
5193  *
5194  * Originally Released Under LGPL - original licence link has changed is not relivant.
5195  *
5196  * Fork - LGPL
5197  * <script type="text/javascript">
5198  */
5199  
5200
5201 /**
5202  * @class Roo.grid.ColumnModel
5203  * @extends Roo.util.Observable
5204  * This is the default implementation of a ColumnModel used by the Grid. It defines
5205  * the columns in the grid.
5206  * <br>Usage:<br>
5207  <pre><code>
5208  var colModel = new Roo.grid.ColumnModel([
5209         {header: "Ticker", width: 60, sortable: true, locked: true},
5210         {header: "Company Name", width: 150, sortable: true},
5211         {header: "Market Cap.", width: 100, sortable: true},
5212         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5213         {header: "Employees", width: 100, sortable: true, resizable: false}
5214  ]);
5215  </code></pre>
5216  * <p>
5217  
5218  * The config options listed for this class are options which may appear in each
5219  * individual column definition.
5220  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5221  * @constructor
5222  * @param {Object} config An Array of column config objects. See this class's
5223  * config objects for details.
5224 */
5225 Roo.grid.ColumnModel = function(config){
5226         /**
5227      * The config passed into the constructor
5228      */
5229     this.config = config;
5230     this.lookup = {};
5231
5232     // if no id, create one
5233     // if the column does not have a dataIndex mapping,
5234     // map it to the order it is in the config
5235     for(var i = 0, len = config.length; i < len; i++){
5236         var c = config[i];
5237         if(typeof c.dataIndex == "undefined"){
5238             c.dataIndex = i;
5239         }
5240         if(typeof c.renderer == "string"){
5241             c.renderer = Roo.util.Format[c.renderer];
5242         }
5243         if(typeof c.id == "undefined"){
5244             c.id = Roo.id();
5245         }
5246         if(c.editor && c.editor.xtype){
5247             c.editor  = Roo.factory(c.editor, Roo.grid);
5248         }
5249         if(c.editor && c.editor.isFormField){
5250             c.editor = new Roo.grid.GridEditor(c.editor);
5251         }
5252         this.lookup[c.id] = c;
5253     }
5254
5255     /**
5256      * The width of columns which have no width specified (defaults to 100)
5257      * @type Number
5258      */
5259     this.defaultWidth = 100;
5260
5261     /**
5262      * Default sortable of columns which have no sortable specified (defaults to false)
5263      * @type Boolean
5264      */
5265     this.defaultSortable = false;
5266
5267     this.addEvents({
5268         /**
5269              * @event widthchange
5270              * Fires when the width of a column changes.
5271              * @param {ColumnModel} this
5272              * @param {Number} columnIndex The column index
5273              * @param {Number} newWidth The new width
5274              */
5275             "widthchange": true,
5276         /**
5277              * @event headerchange
5278              * Fires when the text of a header changes.
5279              * @param {ColumnModel} this
5280              * @param {Number} columnIndex The column index
5281              * @param {Number} newText The new header text
5282              */
5283             "headerchange": true,
5284         /**
5285              * @event hiddenchange
5286              * Fires when a column is hidden or "unhidden".
5287              * @param {ColumnModel} this
5288              * @param {Number} columnIndex The column index
5289              * @param {Boolean} hidden true if hidden, false otherwise
5290              */
5291             "hiddenchange": true,
5292             /**
5293          * @event columnmoved
5294          * Fires when a column is moved.
5295          * @param {ColumnModel} this
5296          * @param {Number} oldIndex
5297          * @param {Number} newIndex
5298          */
5299         "columnmoved" : true,
5300         /**
5301          * @event columlockchange
5302          * Fires when a column's locked state is changed
5303          * @param {ColumnModel} this
5304          * @param {Number} colIndex
5305          * @param {Boolean} locked true if locked
5306          */
5307         "columnlockchange" : true
5308     });
5309     Roo.grid.ColumnModel.superclass.constructor.call(this);
5310 };
5311 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5312     /**
5313      * @cfg {String} header The header text to display in the Grid view.
5314      */
5315     /**
5316      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5317      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5318      * specified, the column's index is used as an index into the Record's data Array.
5319      */
5320     /**
5321      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5322      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5323      */
5324     /**
5325      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5326      * Defaults to the value of the {@link #defaultSortable} property.
5327      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5328      */
5329     /**
5330      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5331      */
5332     /**
5333      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5334      */
5335     /**
5336      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5337      */
5338     /**
5339      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5340      */
5341     /**
5342      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5343      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5344      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5345      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5346      */
5347        /**
5348      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5349      */
5350     /**
5351      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5352      */
5353     /**
5354      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5355      */
5356     /**
5357      * @cfg {String} cursor (Optional)
5358      */
5359     /**
5360      * @cfg {String} tooltip (Optional)
5361      */
5362     /**
5363      * @cfg {Number} xs (Optional)
5364      */
5365     /**
5366      * @cfg {Number} sm (Optional)
5367      */
5368     /**
5369      * @cfg {Number} md (Optional)
5370      */
5371     /**
5372      * @cfg {Number} lg (Optional)
5373      */
5374     /**
5375      * Returns the id of the column at the specified index.
5376      * @param {Number} index The column index
5377      * @return {String} the id
5378      */
5379     getColumnId : function(index){
5380         return this.config[index].id;
5381     },
5382
5383     /**
5384      * Returns the column for a specified id.
5385      * @param {String} id The column id
5386      * @return {Object} the column
5387      */
5388     getColumnById : function(id){
5389         return this.lookup[id];
5390     },
5391
5392     
5393     /**
5394      * Returns the column for a specified dataIndex.
5395      * @param {String} dataIndex The column dataIndex
5396      * @return {Object|Boolean} the column or false if not found
5397      */
5398     getColumnByDataIndex: function(dataIndex){
5399         var index = this.findColumnIndex(dataIndex);
5400         return index > -1 ? this.config[index] : false;
5401     },
5402     
5403     /**
5404      * Returns the index for a specified column id.
5405      * @param {String} id The column id
5406      * @return {Number} the index, or -1 if not found
5407      */
5408     getIndexById : function(id){
5409         for(var i = 0, len = this.config.length; i < len; i++){
5410             if(this.config[i].id == id){
5411                 return i;
5412             }
5413         }
5414         return -1;
5415     },
5416     
5417     /**
5418      * Returns the index for a specified column dataIndex.
5419      * @param {String} dataIndex The column dataIndex
5420      * @return {Number} the index, or -1 if not found
5421      */
5422     
5423     findColumnIndex : function(dataIndex){
5424         for(var i = 0, len = this.config.length; i < len; i++){
5425             if(this.config[i].dataIndex == dataIndex){
5426                 return i;
5427             }
5428         }
5429         return -1;
5430     },
5431     
5432     
5433     moveColumn : function(oldIndex, newIndex){
5434         var c = this.config[oldIndex];
5435         this.config.splice(oldIndex, 1);
5436         this.config.splice(newIndex, 0, c);
5437         this.dataMap = null;
5438         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5439     },
5440
5441     isLocked : function(colIndex){
5442         return this.config[colIndex].locked === true;
5443     },
5444
5445     setLocked : function(colIndex, value, suppressEvent){
5446         if(this.isLocked(colIndex) == value){
5447             return;
5448         }
5449         this.config[colIndex].locked = value;
5450         if(!suppressEvent){
5451             this.fireEvent("columnlockchange", this, colIndex, value);
5452         }
5453     },
5454
5455     getTotalLockedWidth : function(){
5456         var totalWidth = 0;
5457         for(var i = 0; i < this.config.length; i++){
5458             if(this.isLocked(i) && !this.isHidden(i)){
5459                 this.totalWidth += this.getColumnWidth(i);
5460             }
5461         }
5462         return totalWidth;
5463     },
5464
5465     getLockedCount : function(){
5466         for(var i = 0, len = this.config.length; i < len; i++){
5467             if(!this.isLocked(i)){
5468                 return i;
5469             }
5470         }
5471         
5472         return this.config.length;
5473     },
5474
5475     /**
5476      * Returns the number of columns.
5477      * @return {Number}
5478      */
5479     getColumnCount : function(visibleOnly){
5480         if(visibleOnly === true){
5481             var c = 0;
5482             for(var i = 0, len = this.config.length; i < len; i++){
5483                 if(!this.isHidden(i)){
5484                     c++;
5485                 }
5486             }
5487             return c;
5488         }
5489         return this.config.length;
5490     },
5491
5492     /**
5493      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5494      * @param {Function} fn
5495      * @param {Object} scope (optional)
5496      * @return {Array} result
5497      */
5498     getColumnsBy : function(fn, scope){
5499         var r = [];
5500         for(var i = 0, len = this.config.length; i < len; i++){
5501             var c = this.config[i];
5502             if(fn.call(scope||this, c, i) === true){
5503                 r[r.length] = c;
5504             }
5505         }
5506         return r;
5507     },
5508
5509     /**
5510      * Returns true if the specified column is sortable.
5511      * @param {Number} col The column index
5512      * @return {Boolean}
5513      */
5514     isSortable : function(col){
5515         if(typeof this.config[col].sortable == "undefined"){
5516             return this.defaultSortable;
5517         }
5518         return this.config[col].sortable;
5519     },
5520
5521     /**
5522      * Returns the rendering (formatting) function defined for the column.
5523      * @param {Number} col The column index.
5524      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5525      */
5526     getRenderer : function(col){
5527         if(!this.config[col].renderer){
5528             return Roo.grid.ColumnModel.defaultRenderer;
5529         }
5530         return this.config[col].renderer;
5531     },
5532
5533     /**
5534      * Sets the rendering (formatting) function for a column.
5535      * @param {Number} col The column index
5536      * @param {Function} fn The function to use to process the cell's raw data
5537      * to return HTML markup for the grid view. The render function is called with
5538      * the following parameters:<ul>
5539      * <li>Data value.</li>
5540      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5541      * <li>css A CSS style string to apply to the table cell.</li>
5542      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5543      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5544      * <li>Row index</li>
5545      * <li>Column index</li>
5546      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5547      */
5548     setRenderer : function(col, fn){
5549         this.config[col].renderer = fn;
5550     },
5551
5552     /**
5553      * Returns the width for the specified column.
5554      * @param {Number} col The column index
5555      * @return {Number}
5556      */
5557     getColumnWidth : function(col){
5558         return this.config[col].width * 1 || this.defaultWidth;
5559     },
5560
5561     /**
5562      * Sets the width for a column.
5563      * @param {Number} col The column index
5564      * @param {Number} width The new width
5565      */
5566     setColumnWidth : function(col, width, suppressEvent){
5567         this.config[col].width = width;
5568         this.totalWidth = null;
5569         if(!suppressEvent){
5570              this.fireEvent("widthchange", this, col, width);
5571         }
5572     },
5573
5574     /**
5575      * Returns the total width of all columns.
5576      * @param {Boolean} includeHidden True to include hidden column widths
5577      * @return {Number}
5578      */
5579     getTotalWidth : function(includeHidden){
5580         if(!this.totalWidth){
5581             this.totalWidth = 0;
5582             for(var i = 0, len = this.config.length; i < len; i++){
5583                 if(includeHidden || !this.isHidden(i)){
5584                     this.totalWidth += this.getColumnWidth(i);
5585                 }
5586             }
5587         }
5588         return this.totalWidth;
5589     },
5590
5591     /**
5592      * Returns the header for the specified column.
5593      * @param {Number} col The column index
5594      * @return {String}
5595      */
5596     getColumnHeader : function(col){
5597         return this.config[col].header;
5598     },
5599
5600     /**
5601      * Sets the header for a column.
5602      * @param {Number} col The column index
5603      * @param {String} header The new header
5604      */
5605     setColumnHeader : function(col, header){
5606         this.config[col].header = header;
5607         this.fireEvent("headerchange", this, col, header);
5608     },
5609
5610     /**
5611      * Returns the tooltip for the specified column.
5612      * @param {Number} col The column index
5613      * @return {String}
5614      */
5615     getColumnTooltip : function(col){
5616             return this.config[col].tooltip;
5617     },
5618     /**
5619      * Sets the tooltip for a column.
5620      * @param {Number} col The column index
5621      * @param {String} tooltip The new tooltip
5622      */
5623     setColumnTooltip : function(col, tooltip){
5624             this.config[col].tooltip = tooltip;
5625     },
5626
5627     /**
5628      * Returns the dataIndex for the specified column.
5629      * @param {Number} col The column index
5630      * @return {Number}
5631      */
5632     getDataIndex : function(col){
5633         return this.config[col].dataIndex;
5634     },
5635
5636     /**
5637      * Sets the dataIndex for a column.
5638      * @param {Number} col The column index
5639      * @param {Number} dataIndex The new dataIndex
5640      */
5641     setDataIndex : function(col, dataIndex){
5642         this.config[col].dataIndex = dataIndex;
5643     },
5644
5645     
5646     
5647     /**
5648      * Returns true if the cell is editable.
5649      * @param {Number} colIndex The column index
5650      * @param {Number} rowIndex The row index - this is nto actually used..?
5651      * @return {Boolean}
5652      */
5653     isCellEditable : function(colIndex, rowIndex){
5654         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5655     },
5656
5657     /**
5658      * Returns the editor defined for the cell/column.
5659      * return false or null to disable editing.
5660      * @param {Number} colIndex The column index
5661      * @param {Number} rowIndex The row index
5662      * @return {Object}
5663      */
5664     getCellEditor : function(colIndex, rowIndex){
5665         return this.config[colIndex].editor;
5666     },
5667
5668     /**
5669      * Sets if a column is editable.
5670      * @param {Number} col The column index
5671      * @param {Boolean} editable True if the column is editable
5672      */
5673     setEditable : function(col, editable){
5674         this.config[col].editable = editable;
5675     },
5676
5677
5678     /**
5679      * Returns true if the column is hidden.
5680      * @param {Number} colIndex The column index
5681      * @return {Boolean}
5682      */
5683     isHidden : function(colIndex){
5684         return this.config[colIndex].hidden;
5685     },
5686
5687
5688     /**
5689      * Returns true if the column width cannot be changed
5690      */
5691     isFixed : function(colIndex){
5692         return this.config[colIndex].fixed;
5693     },
5694
5695     /**
5696      * Returns true if the column can be resized
5697      * @return {Boolean}
5698      */
5699     isResizable : function(colIndex){
5700         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5701     },
5702     /**
5703      * Sets if a column is hidden.
5704      * @param {Number} colIndex The column index
5705      * @param {Boolean} hidden True if the column is hidden
5706      */
5707     setHidden : function(colIndex, hidden){
5708         this.config[colIndex].hidden = hidden;
5709         this.totalWidth = null;
5710         this.fireEvent("hiddenchange", this, colIndex, hidden);
5711     },
5712
5713     /**
5714      * Sets the editor for a column.
5715      * @param {Number} col The column index
5716      * @param {Object} editor The editor object
5717      */
5718     setEditor : function(col, editor){
5719         this.config[col].editor = editor;
5720     }
5721 });
5722
5723 Roo.grid.ColumnModel.defaultRenderer = function(value)
5724 {
5725     if(typeof value == "object") {
5726         return value;
5727     }
5728         if(typeof value == "string" && value.length < 1){
5729             return "&#160;";
5730         }
5731     
5732         return String.format("{0}", value);
5733 };
5734
5735 // Alias for backwards compatibility
5736 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5737 /*
5738  * Based on:
5739  * Ext JS Library 1.1.1
5740  * Copyright(c) 2006-2007, Ext JS, LLC.
5741  *
5742  * Originally Released Under LGPL - original licence link has changed is not relivant.
5743  *
5744  * Fork - LGPL
5745  * <script type="text/javascript">
5746  */
5747  
5748 /**
5749  * @class Roo.LoadMask
5750  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5751  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5752  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5753  * element's UpdateManager load indicator and will be destroyed after the initial load.
5754  * @constructor
5755  * Create a new LoadMask
5756  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5757  * @param {Object} config The config object
5758  */
5759 Roo.LoadMask = function(el, config){
5760     this.el = Roo.get(el);
5761     Roo.apply(this, config);
5762     if(this.store){
5763         this.store.on('beforeload', this.onBeforeLoad, this);
5764         this.store.on('load', this.onLoad, this);
5765         this.store.on('loadexception', this.onLoadException, this);
5766         this.removeMask = false;
5767     }else{
5768         var um = this.el.getUpdateManager();
5769         um.showLoadIndicator = false; // disable the default indicator
5770         um.on('beforeupdate', this.onBeforeLoad, this);
5771         um.on('update', this.onLoad, this);
5772         um.on('failure', this.onLoad, this);
5773         this.removeMask = true;
5774     }
5775 };
5776
5777 Roo.LoadMask.prototype = {
5778     /**
5779      * @cfg {Boolean} removeMask
5780      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5781      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5782      */
5783     /**
5784      * @cfg {String} msg
5785      * The text to display in a centered loading message box (defaults to 'Loading...')
5786      */
5787     msg : 'Loading...',
5788     /**
5789      * @cfg {String} msgCls
5790      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5791      */
5792     msgCls : 'x-mask-loading',
5793
5794     /**
5795      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5796      * @type Boolean
5797      */
5798     disabled: false,
5799
5800     /**
5801      * Disables the mask to prevent it from being displayed
5802      */
5803     disable : function(){
5804        this.disabled = true;
5805     },
5806
5807     /**
5808      * Enables the mask so that it can be displayed
5809      */
5810     enable : function(){
5811         this.disabled = false;
5812     },
5813     
5814     onLoadException : function()
5815     {
5816         Roo.log(arguments);
5817         
5818         if (typeof(arguments[3]) != 'undefined') {
5819             Roo.MessageBox.alert("Error loading",arguments[3]);
5820         } 
5821         /*
5822         try {
5823             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5824                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5825             }   
5826         } catch(e) {
5827             
5828         }
5829         */
5830     
5831         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5832     },
5833     // private
5834     onLoad : function()
5835     {
5836         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5837     },
5838
5839     // private
5840     onBeforeLoad : function(){
5841         if(!this.disabled){
5842             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5843         }
5844     },
5845
5846     // private
5847     destroy : function(){
5848         if(this.store){
5849             this.store.un('beforeload', this.onBeforeLoad, this);
5850             this.store.un('load', this.onLoad, this);
5851             this.store.un('loadexception', this.onLoadException, this);
5852         }else{
5853             var um = this.el.getUpdateManager();
5854             um.un('beforeupdate', this.onBeforeLoad, this);
5855             um.un('update', this.onLoad, this);
5856             um.un('failure', this.onLoad, this);
5857         }
5858     }
5859 };/*
5860  * - LGPL
5861  *
5862  * table
5863  * 
5864  */
5865
5866 /**
5867  * @class Roo.bootstrap.Table
5868  * @extends Roo.bootstrap.Component
5869  * Bootstrap Table class
5870  * @cfg {String} cls table class
5871  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5872  * @cfg {String} bgcolor Specifies the background color for a table
5873  * @cfg {Number} border Specifies whether the table cells should have borders or not
5874  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5875  * @cfg {Number} cellspacing Specifies the space between cells
5876  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5877  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5878  * @cfg {String} sortable Specifies that the table should be sortable
5879  * @cfg {String} summary Specifies a summary of the content of a table
5880  * @cfg {Number} width Specifies the width of a table
5881  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5882  * 
5883  * @cfg {boolean} striped Should the rows be alternative striped
5884  * @cfg {boolean} bordered Add borders to the table
5885  * @cfg {boolean} hover Add hover highlighting
5886  * @cfg {boolean} condensed Format condensed
5887  * @cfg {boolean} responsive Format condensed
5888  * @cfg {Boolean} loadMask (true|false) default false
5889  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5890  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5891  * @cfg {Boolean} rowSelection (true|false) default false
5892  * @cfg {Boolean} cellSelection (true|false) default false
5893  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5894  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5895  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5896  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5897  
5898  * 
5899  * @constructor
5900  * Create a new Table
5901  * @param {Object} config The config object
5902  */
5903
5904 Roo.bootstrap.Table = function(config){
5905     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5906     
5907   
5908     
5909     // BC...
5910     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5911     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5912     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5913     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5914     
5915     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5916     if (this.sm) {
5917         this.sm.grid = this;
5918         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5919         this.sm = this.selModel;
5920         this.sm.xmodule = this.xmodule || false;
5921     }
5922     
5923     if (this.cm && typeof(this.cm.config) == 'undefined') {
5924         this.colModel = new Roo.grid.ColumnModel(this.cm);
5925         this.cm = this.colModel;
5926         this.cm.xmodule = this.xmodule || false;
5927     }
5928     if (this.store) {
5929         this.store= Roo.factory(this.store, Roo.data);
5930         this.ds = this.store;
5931         this.ds.xmodule = this.xmodule || false;
5932          
5933     }
5934     if (this.footer && this.store) {
5935         this.footer.dataSource = this.ds;
5936         this.footer = Roo.factory(this.footer);
5937     }
5938     
5939     /** @private */
5940     this.addEvents({
5941         /**
5942          * @event cellclick
5943          * Fires when a cell is clicked
5944          * @param {Roo.bootstrap.Table} this
5945          * @param {Roo.Element} el
5946          * @param {Number} rowIndex
5947          * @param {Number} columnIndex
5948          * @param {Roo.EventObject} e
5949          */
5950         "cellclick" : true,
5951         /**
5952          * @event celldblclick
5953          * Fires when a cell is double clicked
5954          * @param {Roo.bootstrap.Table} this
5955          * @param {Roo.Element} el
5956          * @param {Number} rowIndex
5957          * @param {Number} columnIndex
5958          * @param {Roo.EventObject} e
5959          */
5960         "celldblclick" : true,
5961         /**
5962          * @event rowclick
5963          * Fires when a row is clicked
5964          * @param {Roo.bootstrap.Table} this
5965          * @param {Roo.Element} el
5966          * @param {Number} rowIndex
5967          * @param {Roo.EventObject} e
5968          */
5969         "rowclick" : true,
5970         /**
5971          * @event rowdblclick
5972          * Fires when a row is double clicked
5973          * @param {Roo.bootstrap.Table} this
5974          * @param {Roo.Element} el
5975          * @param {Number} rowIndex
5976          * @param {Roo.EventObject} e
5977          */
5978         "rowdblclick" : true,
5979         /**
5980          * @event mouseover
5981          * Fires when a mouseover occur
5982          * @param {Roo.bootstrap.Table} this
5983          * @param {Roo.Element} el
5984          * @param {Number} rowIndex
5985          * @param {Number} columnIndex
5986          * @param {Roo.EventObject} e
5987          */
5988         "mouseover" : true,
5989         /**
5990          * @event mouseout
5991          * Fires when a mouseout occur
5992          * @param {Roo.bootstrap.Table} this
5993          * @param {Roo.Element} el
5994          * @param {Number} rowIndex
5995          * @param {Number} columnIndex
5996          * @param {Roo.EventObject} e
5997          */
5998         "mouseout" : true,
5999         /**
6000          * @event rowclass
6001          * Fires when a row is rendered, so you can change add a style to it.
6002          * @param {Roo.bootstrap.Table} this
6003          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6004          */
6005         'rowclass' : true,
6006           /**
6007          * @event rowsrendered
6008          * Fires when all the  rows have been rendered
6009          * @param {Roo.bootstrap.Table} this
6010          */
6011         'rowsrendered' : true,
6012         /**
6013          * @event contextmenu
6014          * The raw contextmenu event for the entire grid.
6015          * @param {Roo.EventObject} e
6016          */
6017         "contextmenu" : true,
6018         /**
6019          * @event rowcontextmenu
6020          * Fires when a row is right clicked
6021          * @param {Roo.bootstrap.Table} this
6022          * @param {Number} rowIndex
6023          * @param {Roo.EventObject} e
6024          */
6025         "rowcontextmenu" : true,
6026         /**
6027          * @event cellcontextmenu
6028          * Fires when a cell is right clicked
6029          * @param {Roo.bootstrap.Table} this
6030          * @param {Number} rowIndex
6031          * @param {Number} cellIndex
6032          * @param {Roo.EventObject} e
6033          */
6034          "cellcontextmenu" : true,
6035          /**
6036          * @event headercontextmenu
6037          * Fires when a header is right clicked
6038          * @param {Roo.bootstrap.Table} this
6039          * @param {Number} columnIndex
6040          * @param {Roo.EventObject} e
6041          */
6042         "headercontextmenu" : true
6043     });
6044 };
6045
6046 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6047     
6048     cls: false,
6049     align: false,
6050     bgcolor: false,
6051     border: false,
6052     cellpadding: false,
6053     cellspacing: false,
6054     frame: false,
6055     rules: false,
6056     sortable: false,
6057     summary: false,
6058     width: false,
6059     striped : false,
6060     scrollBody : false,
6061     bordered: false,
6062     hover:  false,
6063     condensed : false,
6064     responsive : false,
6065     sm : false,
6066     cm : false,
6067     store : false,
6068     loadMask : false,
6069     footerShow : true,
6070     headerShow : true,
6071   
6072     rowSelection : false,
6073     cellSelection : false,
6074     layout : false,
6075     
6076     // Roo.Element - the tbody
6077     mainBody: false,
6078     // Roo.Element - thead element
6079     mainHead: false,
6080     
6081     container: false, // used by gridpanel...
6082     
6083     lazyLoad : false,
6084     
6085     CSS : Roo.util.CSS,
6086     
6087     auto_hide_footer : false,
6088     
6089     getAutoCreate : function()
6090     {
6091         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6092         
6093         cfg = {
6094             tag: 'table',
6095             cls : 'table',
6096             cn : []
6097         };
6098         if (this.scrollBody) {
6099             cfg.cls += ' table-body-fixed';
6100         }    
6101         if (this.striped) {
6102             cfg.cls += ' table-striped';
6103         }
6104         
6105         if (this.hover) {
6106             cfg.cls += ' table-hover';
6107         }
6108         if (this.bordered) {
6109             cfg.cls += ' table-bordered';
6110         }
6111         if (this.condensed) {
6112             cfg.cls += ' table-condensed';
6113         }
6114         if (this.responsive) {
6115             cfg.cls += ' table-responsive';
6116         }
6117         
6118         if (this.cls) {
6119             cfg.cls+=  ' ' +this.cls;
6120         }
6121         
6122         // this lot should be simplifed...
6123         var _t = this;
6124         var cp = [
6125             'align',
6126             'bgcolor',
6127             'border',
6128             'cellpadding',
6129             'cellspacing',
6130             'frame',
6131             'rules',
6132             'sortable',
6133             'summary',
6134             'width'
6135         ].forEach(function(k) {
6136             if (_t[k]) {
6137                 cfg[k] = _t[k];
6138             }
6139         });
6140         
6141         
6142         if (this.layout) {
6143             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6144         }
6145         
6146         if(this.store || this.cm){
6147             if(this.headerShow){
6148                 cfg.cn.push(this.renderHeader());
6149             }
6150             
6151             cfg.cn.push(this.renderBody());
6152             
6153             if(this.footerShow){
6154                 cfg.cn.push(this.renderFooter());
6155             }
6156             // where does this come from?
6157             //cfg.cls+=  ' TableGrid';
6158         }
6159         
6160         return { cn : [ cfg ] };
6161     },
6162     
6163     initEvents : function()
6164     {   
6165         if(!this.store || !this.cm){
6166             return;
6167         }
6168         if (this.selModel) {
6169             this.selModel.initEvents();
6170         }
6171         
6172         
6173         //Roo.log('initEvents with ds!!!!');
6174         
6175         this.mainBody = this.el.select('tbody', true).first();
6176         this.mainHead = this.el.select('thead', true).first();
6177         this.mainFoot = this.el.select('tfoot', true).first();
6178         
6179         
6180         
6181         var _this = this;
6182         
6183         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6184             e.on('click', _this.sort, _this);
6185         });
6186         
6187         this.mainBody.on("click", this.onClick, this);
6188         this.mainBody.on("dblclick", this.onDblClick, this);
6189         
6190         // why is this done????? = it breaks dialogs??
6191         //this.parent().el.setStyle('position', 'relative');
6192         
6193         
6194         if (this.footer) {
6195             this.footer.parentId = this.id;
6196             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6197             
6198             if(this.lazyLoad){
6199                 this.el.select('tfoot tr td').first().addClass('hide');
6200             }
6201         } 
6202         
6203         if(this.loadMask) {
6204             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6205         }
6206         
6207         this.store.on('load', this.onLoad, this);
6208         this.store.on('beforeload', this.onBeforeLoad, this);
6209         this.store.on('update', this.onUpdate, this);
6210         this.store.on('add', this.onAdd, this);
6211         this.store.on("clear", this.clear, this);
6212         
6213         this.el.on("contextmenu", this.onContextMenu, this);
6214         
6215         this.mainBody.on('scroll', this.onBodyScroll, this);
6216         
6217         this.cm.on("headerchange", this.onHeaderChange, this);
6218         
6219         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6220         
6221     },
6222     
6223     onContextMenu : function(e, t)
6224     {
6225         this.processEvent("contextmenu", e);
6226     },
6227     
6228     processEvent : function(name, e)
6229     {
6230         if (name != 'touchstart' ) {
6231             this.fireEvent(name, e);    
6232         }
6233         
6234         var t = e.getTarget();
6235         
6236         var cell = Roo.get(t);
6237         
6238         if(!cell){
6239             return;
6240         }
6241         
6242         if(cell.findParent('tfoot', false, true)){
6243             return;
6244         }
6245         
6246         if(cell.findParent('thead', false, true)){
6247             
6248             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6249                 cell = Roo.get(t).findParent('th', false, true);
6250                 if (!cell) {
6251                     Roo.log("failed to find th in thead?");
6252                     Roo.log(e.getTarget());
6253                     return;
6254                 }
6255             }
6256             
6257             var cellIndex = cell.dom.cellIndex;
6258             
6259             var ename = name == 'touchstart' ? 'click' : name;
6260             this.fireEvent("header" + ename, this, cellIndex, e);
6261             
6262             return;
6263         }
6264         
6265         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6266             cell = Roo.get(t).findParent('td', false, true);
6267             if (!cell) {
6268                 Roo.log("failed to find th in tbody?");
6269                 Roo.log(e.getTarget());
6270                 return;
6271             }
6272         }
6273         
6274         var row = cell.findParent('tr', false, true);
6275         var cellIndex = cell.dom.cellIndex;
6276         var rowIndex = row.dom.rowIndex - 1;
6277         
6278         if(row !== false){
6279             
6280             this.fireEvent("row" + name, this, rowIndex, e);
6281             
6282             if(cell !== false){
6283             
6284                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6285             }
6286         }
6287         
6288     },
6289     
6290     onMouseover : function(e, el)
6291     {
6292         var cell = Roo.get(el);
6293         
6294         if(!cell){
6295             return;
6296         }
6297         
6298         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6299             cell = cell.findParent('td', false, true);
6300         }
6301         
6302         var row = cell.findParent('tr', false, true);
6303         var cellIndex = cell.dom.cellIndex;
6304         var rowIndex = row.dom.rowIndex - 1; // start from 0
6305         
6306         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6307         
6308     },
6309     
6310     onMouseout : function(e, el)
6311     {
6312         var cell = Roo.get(el);
6313         
6314         if(!cell){
6315             return;
6316         }
6317         
6318         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6319             cell = cell.findParent('td', false, true);
6320         }
6321         
6322         var row = cell.findParent('tr', false, true);
6323         var cellIndex = cell.dom.cellIndex;
6324         var rowIndex = row.dom.rowIndex - 1; // start from 0
6325         
6326         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6327         
6328     },
6329     
6330     onClick : function(e, el)
6331     {
6332         var cell = Roo.get(el);
6333         
6334         if(!cell || (!this.cellSelection && !this.rowSelection)){
6335             return;
6336         }
6337         
6338         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6339             cell = cell.findParent('td', false, true);
6340         }
6341         
6342         if(!cell || typeof(cell) == 'undefined'){
6343             return;
6344         }
6345         
6346         var row = cell.findParent('tr', false, true);
6347         
6348         if(!row || typeof(row) == 'undefined'){
6349             return;
6350         }
6351         
6352         var cellIndex = cell.dom.cellIndex;
6353         var rowIndex = this.getRowIndex(row);
6354         
6355         // why??? - should these not be based on SelectionModel?
6356         if(this.cellSelection){
6357             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6358         }
6359         
6360         if(this.rowSelection){
6361             this.fireEvent('rowclick', this, row, rowIndex, e);
6362         }
6363         
6364         
6365     },
6366         
6367     onDblClick : function(e,el)
6368     {
6369         var cell = Roo.get(el);
6370         
6371         if(!cell || (!this.cellSelection && !this.rowSelection)){
6372             return;
6373         }
6374         
6375         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6376             cell = cell.findParent('td', false, true);
6377         }
6378         
6379         if(!cell || typeof(cell) == 'undefined'){
6380             return;
6381         }
6382         
6383         var row = cell.findParent('tr', false, true);
6384         
6385         if(!row || typeof(row) == 'undefined'){
6386             return;
6387         }
6388         
6389         var cellIndex = cell.dom.cellIndex;
6390         var rowIndex = this.getRowIndex(row);
6391         
6392         if(this.cellSelection){
6393             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6394         }
6395         
6396         if(this.rowSelection){
6397             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6398         }
6399     },
6400     
6401     sort : function(e,el)
6402     {
6403         var col = Roo.get(el);
6404         
6405         if(!col.hasClass('sortable')){
6406             return;
6407         }
6408         
6409         var sort = col.attr('sort');
6410         var dir = 'ASC';
6411         
6412         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6413             dir = 'DESC';
6414         }
6415         
6416         this.store.sortInfo = {field : sort, direction : dir};
6417         
6418         if (this.footer) {
6419             Roo.log("calling footer first");
6420             this.footer.onClick('first');
6421         } else {
6422         
6423             this.store.load({ params : { start : 0 } });
6424         }
6425     },
6426     
6427     renderHeader : function()
6428     {
6429         var header = {
6430             tag: 'thead',
6431             cn : []
6432         };
6433         
6434         var cm = this.cm;
6435         this.totalWidth = 0;
6436         
6437         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6438             
6439             var config = cm.config[i];
6440             
6441             var c = {
6442                 tag: 'th',
6443                 cls : 'x-hcol-' + i,
6444                 style : '',
6445                 html: cm.getColumnHeader(i)
6446             };
6447             
6448             var hh = '';
6449             
6450             if(typeof(config.sortable) != 'undefined' && config.sortable){
6451                 c.cls = 'sortable';
6452                 c.html = '<i class="glyphicon"></i>' + c.html;
6453             }
6454             
6455             if(typeof(config.lgHeader) != 'undefined'){
6456                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6457             }
6458             
6459             if(typeof(config.mdHeader) != 'undefined'){
6460                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6461             }
6462             
6463             if(typeof(config.smHeader) != 'undefined'){
6464                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6465             }
6466             
6467             if(typeof(config.xsHeader) != 'undefined'){
6468                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6469             }
6470             
6471             if(hh.length){
6472                 c.html = hh;
6473             }
6474             
6475             if(typeof(config.tooltip) != 'undefined'){
6476                 c.tooltip = config.tooltip;
6477             }
6478             
6479             if(typeof(config.colspan) != 'undefined'){
6480                 c.colspan = config.colspan;
6481             }
6482             
6483             if(typeof(config.hidden) != 'undefined' && config.hidden){
6484                 c.style += ' display:none;';
6485             }
6486             
6487             if(typeof(config.dataIndex) != 'undefined'){
6488                 c.sort = config.dataIndex;
6489             }
6490             
6491            
6492             
6493             if(typeof(config.align) != 'undefined' && config.align.length){
6494                 c.style += ' text-align:' + config.align + ';';
6495             }
6496             
6497             if(typeof(config.width) != 'undefined'){
6498                 c.style += ' width:' + config.width + 'px;';
6499                 this.totalWidth += config.width;
6500             } else {
6501                 this.totalWidth += 100; // assume minimum of 100 per column?
6502             }
6503             
6504             if(typeof(config.cls) != 'undefined'){
6505                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6506             }
6507             
6508             ['xs','sm','md','lg'].map(function(size){
6509                 
6510                 if(typeof(config[size]) == 'undefined'){
6511                     return;
6512                 }
6513                 
6514                 if (!config[size]) { // 0 = hidden
6515                     c.cls += ' hidden-' + size;
6516                     return;
6517                 }
6518                 
6519                 c.cls += ' col-' + size + '-' + config[size];
6520
6521             });
6522             
6523             header.cn.push(c)
6524         }
6525         
6526         return header;
6527     },
6528     
6529     renderBody : function()
6530     {
6531         var body = {
6532             tag: 'tbody',
6533             cn : [
6534                 {
6535                     tag: 'tr',
6536                     cn : [
6537                         {
6538                             tag : 'td',
6539                             colspan :  this.cm.getColumnCount()
6540                         }
6541                     ]
6542                 }
6543             ]
6544         };
6545         
6546         return body;
6547     },
6548     
6549     renderFooter : function()
6550     {
6551         var footer = {
6552             tag: 'tfoot',
6553             cn : [
6554                 {
6555                     tag: 'tr',
6556                     cn : [
6557                         {
6558                             tag : 'td',
6559                             colspan :  this.cm.getColumnCount()
6560                         }
6561                     ]
6562                 }
6563             ]
6564         };
6565         
6566         return footer;
6567     },
6568     
6569     
6570     
6571     onLoad : function()
6572     {
6573 //        Roo.log('ds onload');
6574         this.clear();
6575         
6576         var _this = this;
6577         var cm = this.cm;
6578         var ds = this.store;
6579         
6580         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6581             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6582             if (_this.store.sortInfo) {
6583                     
6584                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6585                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6586                 }
6587                 
6588                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6589                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6590                 }
6591             }
6592         });
6593         
6594         var tbody =  this.mainBody;
6595               
6596         if(ds.getCount() > 0){
6597             ds.data.each(function(d,rowIndex){
6598                 var row =  this.renderRow(cm, ds, rowIndex);
6599                 
6600                 tbody.createChild(row);
6601                 
6602                 var _this = this;
6603                 
6604                 if(row.cellObjects.length){
6605                     Roo.each(row.cellObjects, function(r){
6606                         _this.renderCellObject(r);
6607                     })
6608                 }
6609                 
6610             }, this);
6611         }
6612         
6613         var tfoot = this.el.select('tfoot', true).first();
6614         
6615         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6616             
6617             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6618             
6619             var total = this.ds.getTotalCount();
6620             
6621             if(this.footer.pageSize < total){
6622                 this.mainFoot.show();
6623             }
6624         }
6625         
6626         Roo.each(this.el.select('tbody td', true).elements, function(e){
6627             e.on('mouseover', _this.onMouseover, _this);
6628         });
6629         
6630         Roo.each(this.el.select('tbody td', true).elements, function(e){
6631             e.on('mouseout', _this.onMouseout, _this);
6632         });
6633         this.fireEvent('rowsrendered', this);
6634         
6635         this.autoSize();
6636     },
6637     
6638     
6639     onUpdate : function(ds,record)
6640     {
6641         this.refreshRow(record);
6642         this.autoSize();
6643     },
6644     
6645     onRemove : function(ds, record, index, isUpdate){
6646         if(isUpdate !== true){
6647             this.fireEvent("beforerowremoved", this, index, record);
6648         }
6649         var bt = this.mainBody.dom;
6650         
6651         var rows = this.el.select('tbody > tr', true).elements;
6652         
6653         if(typeof(rows[index]) != 'undefined'){
6654             bt.removeChild(rows[index].dom);
6655         }
6656         
6657 //        if(bt.rows[index]){
6658 //            bt.removeChild(bt.rows[index]);
6659 //        }
6660         
6661         if(isUpdate !== true){
6662             //this.stripeRows(index);
6663             //this.syncRowHeights(index, index);
6664             //this.layout();
6665             this.fireEvent("rowremoved", this, index, record);
6666         }
6667     },
6668     
6669     onAdd : function(ds, records, rowIndex)
6670     {
6671         //Roo.log('on Add called');
6672         // - note this does not handle multiple adding very well..
6673         var bt = this.mainBody.dom;
6674         for (var i =0 ; i < records.length;i++) {
6675             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6676             //Roo.log(records[i]);
6677             //Roo.log(this.store.getAt(rowIndex+i));
6678             this.insertRow(this.store, rowIndex + i, false);
6679             return;
6680         }
6681         
6682     },
6683     
6684     
6685     refreshRow : function(record){
6686         var ds = this.store, index;
6687         if(typeof record == 'number'){
6688             index = record;
6689             record = ds.getAt(index);
6690         }else{
6691             index = ds.indexOf(record);
6692         }
6693         this.insertRow(ds, index, true);
6694         this.autoSize();
6695         this.onRemove(ds, record, index+1, true);
6696         this.autoSize();
6697         //this.syncRowHeights(index, index);
6698         //this.layout();
6699         this.fireEvent("rowupdated", this, index, record);
6700     },
6701     
6702     insertRow : function(dm, rowIndex, isUpdate){
6703         
6704         if(!isUpdate){
6705             this.fireEvent("beforerowsinserted", this, rowIndex);
6706         }
6707             //var s = this.getScrollState();
6708         var row = this.renderRow(this.cm, this.store, rowIndex);
6709         // insert before rowIndex..
6710         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6711         
6712         var _this = this;
6713                 
6714         if(row.cellObjects.length){
6715             Roo.each(row.cellObjects, function(r){
6716                 _this.renderCellObject(r);
6717             })
6718         }
6719             
6720         if(!isUpdate){
6721             this.fireEvent("rowsinserted", this, rowIndex);
6722             //this.syncRowHeights(firstRow, lastRow);
6723             //this.stripeRows(firstRow);
6724             //this.layout();
6725         }
6726         
6727     },
6728     
6729     
6730     getRowDom : function(rowIndex)
6731     {
6732         var rows = this.el.select('tbody > tr', true).elements;
6733         
6734         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6735         
6736     },
6737     // returns the object tree for a tr..
6738   
6739     
6740     renderRow : function(cm, ds, rowIndex) 
6741     {
6742         var d = ds.getAt(rowIndex);
6743         
6744         var row = {
6745             tag : 'tr',
6746             cls : 'x-row-' + rowIndex,
6747             cn : []
6748         };
6749             
6750         var cellObjects = [];
6751         
6752         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6753             var config = cm.config[i];
6754             
6755             var renderer = cm.getRenderer(i);
6756             var value = '';
6757             var id = false;
6758             
6759             if(typeof(renderer) !== 'undefined'){
6760                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6761             }
6762             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6763             // and are rendered into the cells after the row is rendered - using the id for the element.
6764             
6765             if(typeof(value) === 'object'){
6766                 id = Roo.id();
6767                 cellObjects.push({
6768                     container : id,
6769                     cfg : value 
6770                 })
6771             }
6772             
6773             var rowcfg = {
6774                 record: d,
6775                 rowIndex : rowIndex,
6776                 colIndex : i,
6777                 rowClass : ''
6778             };
6779
6780             this.fireEvent('rowclass', this, rowcfg);
6781             
6782             var td = {
6783                 tag: 'td',
6784                 cls : rowcfg.rowClass + ' x-col-' + i,
6785                 style: '',
6786                 html: (typeof(value) === 'object') ? '' : value
6787             };
6788             
6789             if (id) {
6790                 td.id = id;
6791             }
6792             
6793             if(typeof(config.colspan) != 'undefined'){
6794                 td.colspan = config.colspan;
6795             }
6796             
6797             if(typeof(config.hidden) != 'undefined' && config.hidden){
6798                 td.style += ' display:none;';
6799             }
6800             
6801             if(typeof(config.align) != 'undefined' && config.align.length){
6802                 td.style += ' text-align:' + config.align + ';';
6803             }
6804             if(typeof(config.valign) != 'undefined' && config.valign.length){
6805                 td.style += ' vertical-align:' + config.valign + ';';
6806             }
6807             
6808             if(typeof(config.width) != 'undefined'){
6809                 td.style += ' width:' +  config.width + 'px;';
6810             }
6811             
6812             if(typeof(config.cursor) != 'undefined'){
6813                 td.style += ' cursor:' +  config.cursor + ';';
6814             }
6815             
6816             if(typeof(config.cls) != 'undefined'){
6817                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6818             }
6819             
6820             ['xs','sm','md','lg'].map(function(size){
6821                 
6822                 if(typeof(config[size]) == 'undefined'){
6823                     return;
6824                 }
6825                 
6826                 if (!config[size]) { // 0 = hidden
6827                     td.cls += ' hidden-' + size;
6828                     return;
6829                 }
6830                 
6831                 td.cls += ' col-' + size + '-' + config[size];
6832
6833             });
6834             
6835             row.cn.push(td);
6836            
6837         }
6838         
6839         row.cellObjects = cellObjects;
6840         
6841         return row;
6842           
6843     },
6844     
6845     
6846     
6847     onBeforeLoad : function()
6848     {
6849         
6850     },
6851      /**
6852      * Remove all rows
6853      */
6854     clear : function()
6855     {
6856         this.el.select('tbody', true).first().dom.innerHTML = '';
6857     },
6858     /**
6859      * Show or hide a row.
6860      * @param {Number} rowIndex to show or hide
6861      * @param {Boolean} state hide
6862      */
6863     setRowVisibility : function(rowIndex, state)
6864     {
6865         var bt = this.mainBody.dom;
6866         
6867         var rows = this.el.select('tbody > tr', true).elements;
6868         
6869         if(typeof(rows[rowIndex]) == 'undefined'){
6870             return;
6871         }
6872         rows[rowIndex].dom.style.display = state ? '' : 'none';
6873     },
6874     
6875     
6876     getSelectionModel : function(){
6877         if(!this.selModel){
6878             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6879         }
6880         return this.selModel;
6881     },
6882     /*
6883      * Render the Roo.bootstrap object from renderder
6884      */
6885     renderCellObject : function(r)
6886     {
6887         var _this = this;
6888         
6889         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6890         
6891         var t = r.cfg.render(r.container);
6892         
6893         if(r.cfg.cn){
6894             Roo.each(r.cfg.cn, function(c){
6895                 var child = {
6896                     container: t.getChildContainer(),
6897                     cfg: c
6898                 };
6899                 _this.renderCellObject(child);
6900             })
6901         }
6902     },
6903     
6904     getRowIndex : function(row)
6905     {
6906         var rowIndex = -1;
6907         
6908         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6909             if(el != row){
6910                 return;
6911             }
6912             
6913             rowIndex = index;
6914         });
6915         
6916         return rowIndex;
6917     },
6918      /**
6919      * Returns the grid's underlying element = used by panel.Grid
6920      * @return {Element} The element
6921      */
6922     getGridEl : function(){
6923         return this.el;
6924     },
6925      /**
6926      * Forces a resize - used by panel.Grid
6927      * @return {Element} The element
6928      */
6929     autoSize : function()
6930     {
6931         //var ctr = Roo.get(this.container.dom.parentElement);
6932         var ctr = Roo.get(this.el.dom);
6933         
6934         var thd = this.getGridEl().select('thead',true).first();
6935         var tbd = this.getGridEl().select('tbody', true).first();
6936         var tfd = this.getGridEl().select('tfoot', true).first();
6937         
6938         var cw = ctr.getWidth();
6939         
6940         if (tbd) {
6941             
6942             tbd.setSize(ctr.getWidth(),
6943                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6944             );
6945             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6946             cw -= barsize;
6947         }
6948         cw = Math.max(cw, this.totalWidth);
6949         this.getGridEl().select('tr',true).setWidth(cw);
6950         // resize 'expandable coloumn?
6951         
6952         return; // we doe not have a view in this design..
6953         
6954     },
6955     onBodyScroll: function()
6956     {
6957         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6958         if(this.mainHead){
6959             this.mainHead.setStyle({
6960                 'position' : 'relative',
6961                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6962             });
6963         }
6964         
6965         if(this.lazyLoad){
6966             
6967             var scrollHeight = this.mainBody.dom.scrollHeight;
6968             
6969             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6970             
6971             var height = this.mainBody.getHeight();
6972             
6973             if(scrollHeight - height == scrollTop) {
6974                 
6975                 var total = this.ds.getTotalCount();
6976                 
6977                 if(this.footer.cursor + this.footer.pageSize < total){
6978                     
6979                     this.footer.ds.load({
6980                         params : {
6981                             start : this.footer.cursor + this.footer.pageSize,
6982                             limit : this.footer.pageSize
6983                         },
6984                         add : true
6985                     });
6986                 }
6987             }
6988             
6989         }
6990     },
6991     
6992     onHeaderChange : function()
6993     {
6994         var header = this.renderHeader();
6995         var table = this.el.select('table', true).first();
6996         
6997         this.mainHead.remove();
6998         this.mainHead = table.createChild(header, this.mainBody, false);
6999     },
7000     
7001     onHiddenChange : function(colModel, colIndex, hidden)
7002     {
7003         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7004         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7005         
7006         this.CSS.updateRule(thSelector, "display", "");
7007         this.CSS.updateRule(tdSelector, "display", "");
7008         
7009         if(hidden){
7010             this.CSS.updateRule(thSelector, "display", "none");
7011             this.CSS.updateRule(tdSelector, "display", "none");
7012         }
7013         
7014         this.onHeaderChange();
7015         this.onLoad();
7016         
7017     }
7018     
7019 });
7020
7021  
7022
7023  /*
7024  * - LGPL
7025  *
7026  * table cell
7027  * 
7028  */
7029
7030 /**
7031  * @class Roo.bootstrap.TableCell
7032  * @extends Roo.bootstrap.Component
7033  * Bootstrap TableCell class
7034  * @cfg {String} html cell contain text
7035  * @cfg {String} cls cell class
7036  * @cfg {String} tag cell tag (td|th) default td
7037  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7038  * @cfg {String} align Aligns the content in a cell
7039  * @cfg {String} axis Categorizes cells
7040  * @cfg {String} bgcolor Specifies the background color of a cell
7041  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7042  * @cfg {Number} colspan Specifies the number of columns a cell should span
7043  * @cfg {String} headers Specifies one or more header cells a cell is related to
7044  * @cfg {Number} height Sets the height of a cell
7045  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7046  * @cfg {Number} rowspan Sets the number of rows a cell should span
7047  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7048  * @cfg {String} valign Vertical aligns the content in a cell
7049  * @cfg {Number} width Specifies the width of a cell
7050  * 
7051  * @constructor
7052  * Create a new TableCell
7053  * @param {Object} config The config object
7054  */
7055
7056 Roo.bootstrap.TableCell = function(config){
7057     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7058 };
7059
7060 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7061     
7062     html: false,
7063     cls: false,
7064     tag: false,
7065     abbr: false,
7066     align: false,
7067     axis: false,
7068     bgcolor: false,
7069     charoff: false,
7070     colspan: false,
7071     headers: false,
7072     height: false,
7073     nowrap: false,
7074     rowspan: false,
7075     scope: false,
7076     valign: false,
7077     width: false,
7078     
7079     
7080     getAutoCreate : function(){
7081         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7082         
7083         cfg = {
7084             tag: 'td'
7085         };
7086         
7087         if(this.tag){
7088             cfg.tag = this.tag;
7089         }
7090         
7091         if (this.html) {
7092             cfg.html=this.html
7093         }
7094         if (this.cls) {
7095             cfg.cls=this.cls
7096         }
7097         if (this.abbr) {
7098             cfg.abbr=this.abbr
7099         }
7100         if (this.align) {
7101             cfg.align=this.align
7102         }
7103         if (this.axis) {
7104             cfg.axis=this.axis
7105         }
7106         if (this.bgcolor) {
7107             cfg.bgcolor=this.bgcolor
7108         }
7109         if (this.charoff) {
7110             cfg.charoff=this.charoff
7111         }
7112         if (this.colspan) {
7113             cfg.colspan=this.colspan
7114         }
7115         if (this.headers) {
7116             cfg.headers=this.headers
7117         }
7118         if (this.height) {
7119             cfg.height=this.height
7120         }
7121         if (this.nowrap) {
7122             cfg.nowrap=this.nowrap
7123         }
7124         if (this.rowspan) {
7125             cfg.rowspan=this.rowspan
7126         }
7127         if (this.scope) {
7128             cfg.scope=this.scope
7129         }
7130         if (this.valign) {
7131             cfg.valign=this.valign
7132         }
7133         if (this.width) {
7134             cfg.width=this.width
7135         }
7136         
7137         
7138         return cfg;
7139     }
7140    
7141 });
7142
7143  
7144
7145  /*
7146  * - LGPL
7147  *
7148  * table row
7149  * 
7150  */
7151
7152 /**
7153  * @class Roo.bootstrap.TableRow
7154  * @extends Roo.bootstrap.Component
7155  * Bootstrap TableRow class
7156  * @cfg {String} cls row class
7157  * @cfg {String} align Aligns the content in a table row
7158  * @cfg {String} bgcolor Specifies a background color for a table row
7159  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7160  * @cfg {String} valign Vertical aligns the content in a table row
7161  * 
7162  * @constructor
7163  * Create a new TableRow
7164  * @param {Object} config The config object
7165  */
7166
7167 Roo.bootstrap.TableRow = function(config){
7168     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7169 };
7170
7171 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7172     
7173     cls: false,
7174     align: false,
7175     bgcolor: false,
7176     charoff: false,
7177     valign: false,
7178     
7179     getAutoCreate : function(){
7180         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7181         
7182         cfg = {
7183             tag: 'tr'
7184         };
7185             
7186         if(this.cls){
7187             cfg.cls = this.cls;
7188         }
7189         if(this.align){
7190             cfg.align = this.align;
7191         }
7192         if(this.bgcolor){
7193             cfg.bgcolor = this.bgcolor;
7194         }
7195         if(this.charoff){
7196             cfg.charoff = this.charoff;
7197         }
7198         if(this.valign){
7199             cfg.valign = this.valign;
7200         }
7201         
7202         return cfg;
7203     }
7204    
7205 });
7206
7207  
7208
7209  /*
7210  * - LGPL
7211  *
7212  * table body
7213  * 
7214  */
7215
7216 /**
7217  * @class Roo.bootstrap.TableBody
7218  * @extends Roo.bootstrap.Component
7219  * Bootstrap TableBody class
7220  * @cfg {String} cls element class
7221  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7222  * @cfg {String} align Aligns the content inside the element
7223  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7224  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7225  * 
7226  * @constructor
7227  * Create a new TableBody
7228  * @param {Object} config The config object
7229  */
7230
7231 Roo.bootstrap.TableBody = function(config){
7232     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7233 };
7234
7235 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7236     
7237     cls: false,
7238     tag: false,
7239     align: false,
7240     charoff: false,
7241     valign: false,
7242     
7243     getAutoCreate : function(){
7244         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7245         
7246         cfg = {
7247             tag: 'tbody'
7248         };
7249             
7250         if (this.cls) {
7251             cfg.cls=this.cls
7252         }
7253         if(this.tag){
7254             cfg.tag = this.tag;
7255         }
7256         
7257         if(this.align){
7258             cfg.align = this.align;
7259         }
7260         if(this.charoff){
7261             cfg.charoff = this.charoff;
7262         }
7263         if(this.valign){
7264             cfg.valign = this.valign;
7265         }
7266         
7267         return cfg;
7268     }
7269     
7270     
7271 //    initEvents : function()
7272 //    {
7273 //        
7274 //        if(!this.store){
7275 //            return;
7276 //        }
7277 //        
7278 //        this.store = Roo.factory(this.store, Roo.data);
7279 //        this.store.on('load', this.onLoad, this);
7280 //        
7281 //        this.store.load();
7282 //        
7283 //    },
7284 //    
7285 //    onLoad: function () 
7286 //    {   
7287 //        this.fireEvent('load', this);
7288 //    }
7289 //    
7290 //   
7291 });
7292
7293  
7294
7295  /*
7296  * Based on:
7297  * Ext JS Library 1.1.1
7298  * Copyright(c) 2006-2007, Ext JS, LLC.
7299  *
7300  * Originally Released Under LGPL - original licence link has changed is not relivant.
7301  *
7302  * Fork - LGPL
7303  * <script type="text/javascript">
7304  */
7305
7306 // as we use this in bootstrap.
7307 Roo.namespace('Roo.form');
7308  /**
7309  * @class Roo.form.Action
7310  * Internal Class used to handle form actions
7311  * @constructor
7312  * @param {Roo.form.BasicForm} el The form element or its id
7313  * @param {Object} config Configuration options
7314  */
7315
7316  
7317  
7318 // define the action interface
7319 Roo.form.Action = function(form, options){
7320     this.form = form;
7321     this.options = options || {};
7322 };
7323 /**
7324  * Client Validation Failed
7325  * @const 
7326  */
7327 Roo.form.Action.CLIENT_INVALID = 'client';
7328 /**
7329  * Server Validation Failed
7330  * @const 
7331  */
7332 Roo.form.Action.SERVER_INVALID = 'server';
7333  /**
7334  * Connect to Server Failed
7335  * @const 
7336  */
7337 Roo.form.Action.CONNECT_FAILURE = 'connect';
7338 /**
7339  * Reading Data from Server Failed
7340  * @const 
7341  */
7342 Roo.form.Action.LOAD_FAILURE = 'load';
7343
7344 Roo.form.Action.prototype = {
7345     type : 'default',
7346     failureType : undefined,
7347     response : undefined,
7348     result : undefined,
7349
7350     // interface method
7351     run : function(options){
7352
7353     },
7354
7355     // interface method
7356     success : function(response){
7357
7358     },
7359
7360     // interface method
7361     handleResponse : function(response){
7362
7363     },
7364
7365     // default connection failure
7366     failure : function(response){
7367         
7368         this.response = response;
7369         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7370         this.form.afterAction(this, false);
7371     },
7372
7373     processResponse : function(response){
7374         this.response = response;
7375         if(!response.responseText){
7376             return true;
7377         }
7378         this.result = this.handleResponse(response);
7379         return this.result;
7380     },
7381
7382     // utility functions used internally
7383     getUrl : function(appendParams){
7384         var url = this.options.url || this.form.url || this.form.el.dom.action;
7385         if(appendParams){
7386             var p = this.getParams();
7387             if(p){
7388                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7389             }
7390         }
7391         return url;
7392     },
7393
7394     getMethod : function(){
7395         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7396     },
7397
7398     getParams : function(){
7399         var bp = this.form.baseParams;
7400         var p = this.options.params;
7401         if(p){
7402             if(typeof p == "object"){
7403                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7404             }else if(typeof p == 'string' && bp){
7405                 p += '&' + Roo.urlEncode(bp);
7406             }
7407         }else if(bp){
7408             p = Roo.urlEncode(bp);
7409         }
7410         return p;
7411     },
7412
7413     createCallback : function(){
7414         return {
7415             success: this.success,
7416             failure: this.failure,
7417             scope: this,
7418             timeout: (this.form.timeout*1000),
7419             upload: this.form.fileUpload ? this.success : undefined
7420         };
7421     }
7422 };
7423
7424 Roo.form.Action.Submit = function(form, options){
7425     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7426 };
7427
7428 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7429     type : 'submit',
7430
7431     haveProgress : false,
7432     uploadComplete : false,
7433     
7434     // uploadProgress indicator.
7435     uploadProgress : function()
7436     {
7437         if (!this.form.progressUrl) {
7438             return;
7439         }
7440         
7441         if (!this.haveProgress) {
7442             Roo.MessageBox.progress("Uploading", "Uploading");
7443         }
7444         if (this.uploadComplete) {
7445            Roo.MessageBox.hide();
7446            return;
7447         }
7448         
7449         this.haveProgress = true;
7450    
7451         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7452         
7453         var c = new Roo.data.Connection();
7454         c.request({
7455             url : this.form.progressUrl,
7456             params: {
7457                 id : uid
7458             },
7459             method: 'GET',
7460             success : function(req){
7461                //console.log(data);
7462                 var rdata = false;
7463                 var edata;
7464                 try  {
7465                    rdata = Roo.decode(req.responseText)
7466                 } catch (e) {
7467                     Roo.log("Invalid data from server..");
7468                     Roo.log(edata);
7469                     return;
7470                 }
7471                 if (!rdata || !rdata.success) {
7472                     Roo.log(rdata);
7473                     Roo.MessageBox.alert(Roo.encode(rdata));
7474                     return;
7475                 }
7476                 var data = rdata.data;
7477                 
7478                 if (this.uploadComplete) {
7479                    Roo.MessageBox.hide();
7480                    return;
7481                 }
7482                    
7483                 if (data){
7484                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7485                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7486                     );
7487                 }
7488                 this.uploadProgress.defer(2000,this);
7489             },
7490        
7491             failure: function(data) {
7492                 Roo.log('progress url failed ');
7493                 Roo.log(data);
7494             },
7495             scope : this
7496         });
7497            
7498     },
7499     
7500     
7501     run : function()
7502     {
7503         // run get Values on the form, so it syncs any secondary forms.
7504         this.form.getValues();
7505         
7506         var o = this.options;
7507         var method = this.getMethod();
7508         var isPost = method == 'POST';
7509         if(o.clientValidation === false || this.form.isValid()){
7510             
7511             if (this.form.progressUrl) {
7512                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7513                     (new Date() * 1) + '' + Math.random());
7514                     
7515             } 
7516             
7517             
7518             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7519                 form:this.form.el.dom,
7520                 url:this.getUrl(!isPost),
7521                 method: method,
7522                 params:isPost ? this.getParams() : null,
7523                 isUpload: this.form.fileUpload
7524             }));
7525             
7526             this.uploadProgress();
7527
7528         }else if (o.clientValidation !== false){ // client validation failed
7529             this.failureType = Roo.form.Action.CLIENT_INVALID;
7530             this.form.afterAction(this, false);
7531         }
7532     },
7533
7534     success : function(response)
7535     {
7536         this.uploadComplete= true;
7537         if (this.haveProgress) {
7538             Roo.MessageBox.hide();
7539         }
7540         
7541         
7542         var result = this.processResponse(response);
7543         if(result === true || result.success){
7544             this.form.afterAction(this, true);
7545             return;
7546         }
7547         if(result.errors){
7548             this.form.markInvalid(result.errors);
7549             this.failureType = Roo.form.Action.SERVER_INVALID;
7550         }
7551         this.form.afterAction(this, false);
7552     },
7553     failure : function(response)
7554     {
7555         this.uploadComplete= true;
7556         if (this.haveProgress) {
7557             Roo.MessageBox.hide();
7558         }
7559         
7560         this.response = response;
7561         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7562         this.form.afterAction(this, false);
7563     },
7564     
7565     handleResponse : function(response){
7566         if(this.form.errorReader){
7567             var rs = this.form.errorReader.read(response);
7568             var errors = [];
7569             if(rs.records){
7570                 for(var i = 0, len = rs.records.length; i < len; i++) {
7571                     var r = rs.records[i];
7572                     errors[i] = r.data;
7573                 }
7574             }
7575             if(errors.length < 1){
7576                 errors = null;
7577             }
7578             return {
7579                 success : rs.success,
7580                 errors : errors
7581             };
7582         }
7583         var ret = false;
7584         try {
7585             ret = Roo.decode(response.responseText);
7586         } catch (e) {
7587             ret = {
7588                 success: false,
7589                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7590                 errors : []
7591             };
7592         }
7593         return ret;
7594         
7595     }
7596 });
7597
7598
7599 Roo.form.Action.Load = function(form, options){
7600     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7601     this.reader = this.form.reader;
7602 };
7603
7604 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7605     type : 'load',
7606
7607     run : function(){
7608         
7609         Roo.Ajax.request(Roo.apply(
7610                 this.createCallback(), {
7611                     method:this.getMethod(),
7612                     url:this.getUrl(false),
7613                     params:this.getParams()
7614         }));
7615     },
7616
7617     success : function(response){
7618         
7619         var result = this.processResponse(response);
7620         if(result === true || !result.success || !result.data){
7621             this.failureType = Roo.form.Action.LOAD_FAILURE;
7622             this.form.afterAction(this, false);
7623             return;
7624         }
7625         this.form.clearInvalid();
7626         this.form.setValues(result.data);
7627         this.form.afterAction(this, true);
7628     },
7629
7630     handleResponse : function(response){
7631         if(this.form.reader){
7632             var rs = this.form.reader.read(response);
7633             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7634             return {
7635                 success : rs.success,
7636                 data : data
7637             };
7638         }
7639         return Roo.decode(response.responseText);
7640     }
7641 });
7642
7643 Roo.form.Action.ACTION_TYPES = {
7644     'load' : Roo.form.Action.Load,
7645     'submit' : Roo.form.Action.Submit
7646 };/*
7647  * - LGPL
7648  *
7649  * form
7650  *
7651  */
7652
7653 /**
7654  * @class Roo.bootstrap.Form
7655  * @extends Roo.bootstrap.Component
7656  * Bootstrap Form class
7657  * @cfg {String} method  GET | POST (default POST)
7658  * @cfg {String} labelAlign top | left (default top)
7659  * @cfg {String} align left  | right - for navbars
7660  * @cfg {Boolean} loadMask load mask when submit (default true)
7661
7662  *
7663  * @constructor
7664  * Create a new Form
7665  * @param {Object} config The config object
7666  */
7667
7668
7669 Roo.bootstrap.Form = function(config){
7670     
7671     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7672     
7673     Roo.bootstrap.Form.popover.apply();
7674     
7675     this.addEvents({
7676         /**
7677          * @event clientvalidation
7678          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7679          * @param {Form} this
7680          * @param {Boolean} valid true if the form has passed client-side validation
7681          */
7682         clientvalidation: true,
7683         /**
7684          * @event beforeaction
7685          * Fires before any action is performed. Return false to cancel the action.
7686          * @param {Form} this
7687          * @param {Action} action The action to be performed
7688          */
7689         beforeaction: true,
7690         /**
7691          * @event actionfailed
7692          * Fires when an action fails.
7693          * @param {Form} this
7694          * @param {Action} action The action that failed
7695          */
7696         actionfailed : true,
7697         /**
7698          * @event actioncomplete
7699          * Fires when an action is completed.
7700          * @param {Form} this
7701          * @param {Action} action The action that completed
7702          */
7703         actioncomplete : true
7704     });
7705 };
7706
7707 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7708
7709      /**
7710      * @cfg {String} method
7711      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7712      */
7713     method : 'POST',
7714     /**
7715      * @cfg {String} url
7716      * The URL to use for form actions if one isn't supplied in the action options.
7717      */
7718     /**
7719      * @cfg {Boolean} fileUpload
7720      * Set to true if this form is a file upload.
7721      */
7722
7723     /**
7724      * @cfg {Object} baseParams
7725      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7726      */
7727
7728     /**
7729      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7730      */
7731     timeout: 30,
7732     /**
7733      * @cfg {Sting} align (left|right) for navbar forms
7734      */
7735     align : 'left',
7736
7737     // private
7738     activeAction : null,
7739
7740     /**
7741      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7742      * element by passing it or its id or mask the form itself by passing in true.
7743      * @type Mixed
7744      */
7745     waitMsgTarget : false,
7746
7747     loadMask : true,
7748     
7749     /**
7750      * @cfg {Boolean} errorMask (true|false) default false
7751      */
7752     errorMask : false,
7753     
7754     /**
7755      * @cfg {Number} maskOffset Default 100
7756      */
7757     maskOffset : 100,
7758     
7759     /**
7760      * @cfg {Boolean} maskBody
7761      */
7762     maskBody : false,
7763
7764     getAutoCreate : function(){
7765
7766         var cfg = {
7767             tag: 'form',
7768             method : this.method || 'POST',
7769             id : this.id || Roo.id(),
7770             cls : ''
7771         };
7772         if (this.parent().xtype.match(/^Nav/)) {
7773             cfg.cls = 'navbar-form navbar-' + this.align;
7774
7775         }
7776
7777         if (this.labelAlign == 'left' ) {
7778             cfg.cls += ' form-horizontal';
7779         }
7780
7781
7782         return cfg;
7783     },
7784     initEvents : function()
7785     {
7786         this.el.on('submit', this.onSubmit, this);
7787         // this was added as random key presses on the form where triggering form submit.
7788         this.el.on('keypress', function(e) {
7789             if (e.getCharCode() != 13) {
7790                 return true;
7791             }
7792             // we might need to allow it for textareas.. and some other items.
7793             // check e.getTarget().
7794
7795             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7796                 return true;
7797             }
7798
7799             Roo.log("keypress blocked");
7800
7801             e.preventDefault();
7802             return false;
7803         });
7804         
7805     },
7806     // private
7807     onSubmit : function(e){
7808         e.stopEvent();
7809     },
7810
7811      /**
7812      * Returns true if client-side validation on the form is successful.
7813      * @return Boolean
7814      */
7815     isValid : function(){
7816         var items = this.getItems();
7817         var valid = true;
7818         var target = false;
7819         
7820         items.each(function(f){
7821             
7822             if(f.validate()){
7823                 return;
7824             }
7825             
7826             valid = false;
7827
7828             if(!target && f.el.isVisible(true)){
7829                 target = f;
7830             }
7831            
7832         });
7833         
7834         if(this.errorMask && !valid){
7835             Roo.bootstrap.Form.popover.mask(this, target);
7836         }
7837         
7838         return valid;
7839     },
7840     
7841     /**
7842      * Returns true if any fields in this form have changed since their original load.
7843      * @return Boolean
7844      */
7845     isDirty : function(){
7846         var dirty = false;
7847         var items = this.getItems();
7848         items.each(function(f){
7849            if(f.isDirty()){
7850                dirty = true;
7851                return false;
7852            }
7853            return true;
7854         });
7855         return dirty;
7856     },
7857      /**
7858      * Performs a predefined action (submit or load) or custom actions you define on this form.
7859      * @param {String} actionName The name of the action type
7860      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7861      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7862      * accept other config options):
7863      * <pre>
7864 Property          Type             Description
7865 ----------------  ---------------  ----------------------------------------------------------------------------------
7866 url               String           The url for the action (defaults to the form's url)
7867 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7868 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7869 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7870                                    validate the form on the client (defaults to false)
7871      * </pre>
7872      * @return {BasicForm} this
7873      */
7874     doAction : function(action, options){
7875         if(typeof action == 'string'){
7876             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7877         }
7878         if(this.fireEvent('beforeaction', this, action) !== false){
7879             this.beforeAction(action);
7880             action.run.defer(100, action);
7881         }
7882         return this;
7883     },
7884
7885     // private
7886     beforeAction : function(action){
7887         var o = action.options;
7888         
7889         if(this.loadMask){
7890             
7891             if(this.maskBody){
7892                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7893             } else {
7894                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7895             }
7896         }
7897         // not really supported yet.. ??
7898
7899         //if(this.waitMsgTarget === true){
7900         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7901         //}else if(this.waitMsgTarget){
7902         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7903         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7904         //}else {
7905         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7906        // }
7907
7908     },
7909
7910     // private
7911     afterAction : function(action, success){
7912         this.activeAction = null;
7913         var o = action.options;
7914
7915         if(this.loadMask){
7916             
7917             if(this.maskBody){
7918                 Roo.get(document.body).unmask();
7919             } else {
7920                 this.el.unmask();
7921             }
7922         }
7923         
7924         //if(this.waitMsgTarget === true){
7925 //            this.el.unmask();
7926         //}else if(this.waitMsgTarget){
7927         //    this.waitMsgTarget.unmask();
7928         //}else{
7929         //    Roo.MessageBox.updateProgress(1);
7930         //    Roo.MessageBox.hide();
7931        // }
7932         //
7933         if(success){
7934             if(o.reset){
7935                 this.reset();
7936             }
7937             Roo.callback(o.success, o.scope, [this, action]);
7938             this.fireEvent('actioncomplete', this, action);
7939
7940         }else{
7941
7942             // failure condition..
7943             // we have a scenario where updates need confirming.
7944             // eg. if a locking scenario exists..
7945             // we look for { errors : { needs_confirm : true }} in the response.
7946             if (
7947                 (typeof(action.result) != 'undefined')  &&
7948                 (typeof(action.result.errors) != 'undefined')  &&
7949                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7950            ){
7951                 var _t = this;
7952                 Roo.log("not supported yet");
7953                  /*
7954
7955                 Roo.MessageBox.confirm(
7956                     "Change requires confirmation",
7957                     action.result.errorMsg,
7958                     function(r) {
7959                         if (r != 'yes') {
7960                             return;
7961                         }
7962                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7963                     }
7964
7965                 );
7966                 */
7967
7968
7969                 return;
7970             }
7971
7972             Roo.callback(o.failure, o.scope, [this, action]);
7973             // show an error message if no failed handler is set..
7974             if (!this.hasListener('actionfailed')) {
7975                 Roo.log("need to add dialog support");
7976                 /*
7977                 Roo.MessageBox.alert("Error",
7978                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7979                         action.result.errorMsg :
7980                         "Saving Failed, please check your entries or try again"
7981                 );
7982                 */
7983             }
7984
7985             this.fireEvent('actionfailed', this, action);
7986         }
7987
7988     },
7989     /**
7990      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7991      * @param {String} id The value to search for
7992      * @return Field
7993      */
7994     findField : function(id){
7995         var items = this.getItems();
7996         var field = items.get(id);
7997         if(!field){
7998              items.each(function(f){
7999                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8000                     field = f;
8001                     return false;
8002                 }
8003                 return true;
8004             });
8005         }
8006         return field || null;
8007     },
8008      /**
8009      * Mark fields in this form invalid in bulk.
8010      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8011      * @return {BasicForm} this
8012      */
8013     markInvalid : function(errors){
8014         if(errors instanceof Array){
8015             for(var i = 0, len = errors.length; i < len; i++){
8016                 var fieldError = errors[i];
8017                 var f = this.findField(fieldError.id);
8018                 if(f){
8019                     f.markInvalid(fieldError.msg);
8020                 }
8021             }
8022         }else{
8023             var field, id;
8024             for(id in errors){
8025                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8026                     field.markInvalid(errors[id]);
8027                 }
8028             }
8029         }
8030         //Roo.each(this.childForms || [], function (f) {
8031         //    f.markInvalid(errors);
8032         //});
8033
8034         return this;
8035     },
8036
8037     /**
8038      * Set values for fields in this form in bulk.
8039      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8040      * @return {BasicForm} this
8041      */
8042     setValues : function(values){
8043         if(values instanceof Array){ // array of objects
8044             for(var i = 0, len = values.length; i < len; i++){
8045                 var v = values[i];
8046                 var f = this.findField(v.id);
8047                 if(f){
8048                     f.setValue(v.value);
8049                     if(this.trackResetOnLoad){
8050                         f.originalValue = f.getValue();
8051                     }
8052                 }
8053             }
8054         }else{ // object hash
8055             var field, id;
8056             for(id in values){
8057                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8058
8059                     if (field.setFromData &&
8060                         field.valueField &&
8061                         field.displayField &&
8062                         // combos' with local stores can
8063                         // be queried via setValue()
8064                         // to set their value..
8065                         (field.store && !field.store.isLocal)
8066                         ) {
8067                         // it's a combo
8068                         var sd = { };
8069                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8070                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8071                         field.setFromData(sd);
8072
8073                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8074                         
8075                         field.setFromData(values);
8076                         
8077                     } else {
8078                         field.setValue(values[id]);
8079                     }
8080
8081
8082                     if(this.trackResetOnLoad){
8083                         field.originalValue = field.getValue();
8084                     }
8085                 }
8086             }
8087         }
8088
8089         //Roo.each(this.childForms || [], function (f) {
8090         //    f.setValues(values);
8091         //});
8092
8093         return this;
8094     },
8095
8096     /**
8097      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8098      * they are returned as an array.
8099      * @param {Boolean} asString
8100      * @return {Object}
8101      */
8102     getValues : function(asString){
8103         //if (this.childForms) {
8104             // copy values from the child forms
8105         //    Roo.each(this.childForms, function (f) {
8106         //        this.setValues(f.getValues());
8107         //    }, this);
8108         //}
8109
8110
8111
8112         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8113         if(asString === true){
8114             return fs;
8115         }
8116         return Roo.urlDecode(fs);
8117     },
8118
8119     /**
8120      * Returns the fields in this form as an object with key/value pairs.
8121      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8122      * @return {Object}
8123      */
8124     getFieldValues : function(with_hidden)
8125     {
8126         var items = this.getItems();
8127         var ret = {};
8128         items.each(function(f){
8129             
8130             if (!f.getName()) {
8131                 return;
8132             }
8133             
8134             var v = f.getValue();
8135             
8136             if (f.inputType =='radio') {
8137                 if (typeof(ret[f.getName()]) == 'undefined') {
8138                     ret[f.getName()] = ''; // empty..
8139                 }
8140
8141                 if (!f.el.dom.checked) {
8142                     return;
8143
8144                 }
8145                 v = f.el.dom.value;
8146
8147             }
8148             
8149             if(f.xtype == 'MoneyField'){
8150                 ret[f.currencyName] = f.getCurrency();
8151             }
8152
8153             // not sure if this supported any more..
8154             if ((typeof(v) == 'object') && f.getRawValue) {
8155                 v = f.getRawValue() ; // dates..
8156             }
8157             // combo boxes where name != hiddenName...
8158             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8159                 ret[f.name] = f.getRawValue();
8160             }
8161             ret[f.getName()] = v;
8162         });
8163
8164         return ret;
8165     },
8166
8167     /**
8168      * Clears all invalid messages in this form.
8169      * @return {BasicForm} this
8170      */
8171     clearInvalid : function(){
8172         var items = this.getItems();
8173
8174         items.each(function(f){
8175            f.clearInvalid();
8176         });
8177
8178         return this;
8179     },
8180
8181     /**
8182      * Resets this form.
8183      * @return {BasicForm} this
8184      */
8185     reset : function(){
8186         var items = this.getItems();
8187         items.each(function(f){
8188             f.reset();
8189         });
8190
8191         Roo.each(this.childForms || [], function (f) {
8192             f.reset();
8193         });
8194
8195
8196         return this;
8197     },
8198     
8199     getItems : function()
8200     {
8201         var r=new Roo.util.MixedCollection(false, function(o){
8202             return o.id || (o.id = Roo.id());
8203         });
8204         var iter = function(el) {
8205             if (el.inputEl) {
8206                 r.add(el);
8207             }
8208             if (!el.items) {
8209                 return;
8210             }
8211             Roo.each(el.items,function(e) {
8212                 iter(e);
8213             });
8214         };
8215
8216         iter(this);
8217         return r;
8218     },
8219     
8220     hideFields : function(items)
8221     {
8222         Roo.each(items, function(i){
8223             
8224             var f = this.findField(i);
8225             
8226             if(!f){
8227                 return;
8228             }
8229             
8230             if(f.xtype == 'DateField'){
8231                 f.setVisible(false);
8232                 return;
8233             }
8234             
8235             f.hide();
8236             
8237         }, this);
8238     },
8239     
8240     showFields : function(items)
8241     {
8242         Roo.each(items, function(i){
8243             
8244             var f = this.findField(i);
8245             
8246             if(!f){
8247                 return;
8248             }
8249             
8250             if(f.xtype == 'DateField'){
8251                 f.setVisible(true);
8252                 return;
8253             }
8254             
8255             f.show();
8256             
8257         }, this);
8258     }
8259
8260 });
8261
8262 Roo.apply(Roo.bootstrap.Form, {
8263     
8264     popover : {
8265         
8266         padding : 5,
8267         
8268         isApplied : false,
8269         
8270         isMasked : false,
8271         
8272         form : false,
8273         
8274         target : false,
8275         
8276         toolTip : false,
8277         
8278         intervalID : false,
8279         
8280         maskEl : false,
8281         
8282         apply : function()
8283         {
8284             if(this.isApplied){
8285                 return;
8286             }
8287             
8288             this.maskEl = {
8289                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8290                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8291                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8292                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8293             };
8294             
8295             this.maskEl.top.enableDisplayMode("block");
8296             this.maskEl.left.enableDisplayMode("block");
8297             this.maskEl.bottom.enableDisplayMode("block");
8298             this.maskEl.right.enableDisplayMode("block");
8299             
8300             this.toolTip = new Roo.bootstrap.Tooltip({
8301                 cls : 'roo-form-error-popover',
8302                 alignment : {
8303                     'left' : ['r-l', [-2,0], 'right'],
8304                     'right' : ['l-r', [2,0], 'left'],
8305                     'bottom' : ['tl-bl', [0,2], 'top'],
8306                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8307                 }
8308             });
8309             
8310             this.toolTip.render(Roo.get(document.body));
8311
8312             this.toolTip.el.enableDisplayMode("block");
8313             
8314             Roo.get(document.body).on('click', function(){
8315                 this.unmask();
8316             }, this);
8317             
8318             Roo.get(document.body).on('touchstart', function(){
8319                 this.unmask();
8320             }, this);
8321             
8322             this.isApplied = true
8323         },
8324         
8325         mask : function(form, target)
8326         {
8327             this.form = form;
8328             
8329             this.target = target;
8330             
8331             if(!this.form.errorMask || !target.el){
8332                 return;
8333             }
8334             
8335             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8336             
8337             Roo.log(scrollable);
8338             
8339             var ot = this.target.el.calcOffsetsTo(scrollable);
8340             
8341             var scrollTo = ot[1] - this.form.maskOffset;
8342             
8343             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8344             
8345             scrollable.scrollTo('top', scrollTo);
8346             
8347             var box = this.target.el.getBox();
8348             Roo.log(box);
8349             var zIndex = Roo.bootstrap.Modal.zIndex++;
8350
8351             
8352             this.maskEl.top.setStyle('position', 'absolute');
8353             this.maskEl.top.setStyle('z-index', zIndex);
8354             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8355             this.maskEl.top.setLeft(0);
8356             this.maskEl.top.setTop(0);
8357             this.maskEl.top.show();
8358             
8359             this.maskEl.left.setStyle('position', 'absolute');
8360             this.maskEl.left.setStyle('z-index', zIndex);
8361             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8362             this.maskEl.left.setLeft(0);
8363             this.maskEl.left.setTop(box.y - this.padding);
8364             this.maskEl.left.show();
8365
8366             this.maskEl.bottom.setStyle('position', 'absolute');
8367             this.maskEl.bottom.setStyle('z-index', zIndex);
8368             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8369             this.maskEl.bottom.setLeft(0);
8370             this.maskEl.bottom.setTop(box.bottom + this.padding);
8371             this.maskEl.bottom.show();
8372
8373             this.maskEl.right.setStyle('position', 'absolute');
8374             this.maskEl.right.setStyle('z-index', zIndex);
8375             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8376             this.maskEl.right.setLeft(box.right + this.padding);
8377             this.maskEl.right.setTop(box.y - this.padding);
8378             this.maskEl.right.show();
8379
8380             this.toolTip.bindEl = this.target.el;
8381
8382             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8383
8384             var tip = this.target.blankText;
8385
8386             if(this.target.getValue() !== '' ) {
8387                 
8388                 if (this.target.invalidText.length) {
8389                     tip = this.target.invalidText;
8390                 } else if (this.target.regexText.length){
8391                     tip = this.target.regexText;
8392                 }
8393             }
8394
8395             this.toolTip.show(tip);
8396
8397             this.intervalID = window.setInterval(function() {
8398                 Roo.bootstrap.Form.popover.unmask();
8399             }, 10000);
8400
8401             window.onwheel = function(){ return false;};
8402             
8403             (function(){ this.isMasked = true; }).defer(500, this);
8404             
8405         },
8406         
8407         unmask : function()
8408         {
8409             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8410                 return;
8411             }
8412             
8413             this.maskEl.top.setStyle('position', 'absolute');
8414             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8415             this.maskEl.top.hide();
8416
8417             this.maskEl.left.setStyle('position', 'absolute');
8418             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8419             this.maskEl.left.hide();
8420
8421             this.maskEl.bottom.setStyle('position', 'absolute');
8422             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8423             this.maskEl.bottom.hide();
8424
8425             this.maskEl.right.setStyle('position', 'absolute');
8426             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8427             this.maskEl.right.hide();
8428             
8429             this.toolTip.hide();
8430             
8431             this.toolTip.el.hide();
8432             
8433             window.onwheel = function(){ return true;};
8434             
8435             if(this.intervalID){
8436                 window.clearInterval(this.intervalID);
8437                 this.intervalID = false;
8438             }
8439             
8440             this.isMasked = false;
8441             
8442         }
8443         
8444     }
8445     
8446 });
8447
8448 /*
8449  * Based on:
8450  * Ext JS Library 1.1.1
8451  * Copyright(c) 2006-2007, Ext JS, LLC.
8452  *
8453  * Originally Released Under LGPL - original licence link has changed is not relivant.
8454  *
8455  * Fork - LGPL
8456  * <script type="text/javascript">
8457  */
8458 /**
8459  * @class Roo.form.VTypes
8460  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8461  * @singleton
8462  */
8463 Roo.form.VTypes = function(){
8464     // closure these in so they are only created once.
8465     var alpha = /^[a-zA-Z_]+$/;
8466     var alphanum = /^[a-zA-Z0-9_]+$/;
8467     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8468     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8469
8470     // All these messages and functions are configurable
8471     return {
8472         /**
8473          * The function used to validate email addresses
8474          * @param {String} value The email address
8475          */
8476         'email' : function(v){
8477             return email.test(v);
8478         },
8479         /**
8480          * The error text to display when the email validation function returns false
8481          * @type String
8482          */
8483         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8484         /**
8485          * The keystroke filter mask to be applied on email input
8486          * @type RegExp
8487          */
8488         'emailMask' : /[a-z0-9_\.\-@]/i,
8489
8490         /**
8491          * The function used to validate URLs
8492          * @param {String} value The URL
8493          */
8494         'url' : function(v){
8495             return url.test(v);
8496         },
8497         /**
8498          * The error text to display when the url validation function returns false
8499          * @type String
8500          */
8501         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8502         
8503         /**
8504          * The function used to validate alpha values
8505          * @param {String} value The value
8506          */
8507         'alpha' : function(v){
8508             return alpha.test(v);
8509         },
8510         /**
8511          * The error text to display when the alpha validation function returns false
8512          * @type String
8513          */
8514         'alphaText' : 'This field should only contain letters and _',
8515         /**
8516          * The keystroke filter mask to be applied on alpha input
8517          * @type RegExp
8518          */
8519         'alphaMask' : /[a-z_]/i,
8520
8521         /**
8522          * The function used to validate alphanumeric values
8523          * @param {String} value The value
8524          */
8525         'alphanum' : function(v){
8526             return alphanum.test(v);
8527         },
8528         /**
8529          * The error text to display when the alphanumeric validation function returns false
8530          * @type String
8531          */
8532         'alphanumText' : 'This field should only contain letters, numbers and _',
8533         /**
8534          * The keystroke filter mask to be applied on alphanumeric input
8535          * @type RegExp
8536          */
8537         'alphanumMask' : /[a-z0-9_]/i
8538     };
8539 }();/*
8540  * - LGPL
8541  *
8542  * Input
8543  * 
8544  */
8545
8546 /**
8547  * @class Roo.bootstrap.Input
8548  * @extends Roo.bootstrap.Component
8549  * Bootstrap Input class
8550  * @cfg {Boolean} disabled is it disabled
8551  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8552  * @cfg {String} name name of the input
8553  * @cfg {string} fieldLabel - the label associated
8554  * @cfg {string} placeholder - placeholder to put in text.
8555  * @cfg {string}  before - input group add on before
8556  * @cfg {string} after - input group add on after
8557  * @cfg {string} size - (lg|sm) or leave empty..
8558  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8559  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8560  * @cfg {Number} md colspan out of 12 for computer-sized screens
8561  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8562  * @cfg {string} value default value of the input
8563  * @cfg {Number} labelWidth set the width of label 
8564  * @cfg {Number} labellg set the width of label (1-12)
8565  * @cfg {Number} labelmd set the width of label (1-12)
8566  * @cfg {Number} labelsm set the width of label (1-12)
8567  * @cfg {Number} labelxs set the width of label (1-12)
8568  * @cfg {String} labelAlign (top|left)
8569  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8570  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8571  * @cfg {String} indicatorpos (left|right) default left
8572  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8573  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8574
8575  * @cfg {String} align (left|center|right) Default left
8576  * @cfg {Boolean} forceFeedback (true|false) Default false
8577  * 
8578  * @constructor
8579  * Create a new Input
8580  * @param {Object} config The config object
8581  */
8582
8583 Roo.bootstrap.Input = function(config){
8584     
8585     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8586     
8587     this.addEvents({
8588         /**
8589          * @event focus
8590          * Fires when this field receives input focus.
8591          * @param {Roo.form.Field} this
8592          */
8593         focus : true,
8594         /**
8595          * @event blur
8596          * Fires when this field loses input focus.
8597          * @param {Roo.form.Field} this
8598          */
8599         blur : true,
8600         /**
8601          * @event specialkey
8602          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8603          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8604          * @param {Roo.form.Field} this
8605          * @param {Roo.EventObject} e The event object
8606          */
8607         specialkey : true,
8608         /**
8609          * @event change
8610          * Fires just before the field blurs if the field value has changed.
8611          * @param {Roo.form.Field} this
8612          * @param {Mixed} newValue The new value
8613          * @param {Mixed} oldValue The original value
8614          */
8615         change : true,
8616         /**
8617          * @event invalid
8618          * Fires after the field has been marked as invalid.
8619          * @param {Roo.form.Field} this
8620          * @param {String} msg The validation message
8621          */
8622         invalid : true,
8623         /**
8624          * @event valid
8625          * Fires after the field has been validated with no errors.
8626          * @param {Roo.form.Field} this
8627          */
8628         valid : true,
8629          /**
8630          * @event keyup
8631          * Fires after the key up
8632          * @param {Roo.form.Field} this
8633          * @param {Roo.EventObject}  e The event Object
8634          */
8635         keyup : true
8636     });
8637 };
8638
8639 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8640      /**
8641      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8642       automatic validation (defaults to "keyup").
8643      */
8644     validationEvent : "keyup",
8645      /**
8646      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8647      */
8648     validateOnBlur : true,
8649     /**
8650      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8651      */
8652     validationDelay : 250,
8653      /**
8654      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8655      */
8656     focusClass : "x-form-focus",  // not needed???
8657     
8658        
8659     /**
8660      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8661      */
8662     invalidClass : "has-warning",
8663     
8664     /**
8665      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8666      */
8667     validClass : "has-success",
8668     
8669     /**
8670      * @cfg {Boolean} hasFeedback (true|false) default true
8671      */
8672     hasFeedback : true,
8673     
8674     /**
8675      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8676      */
8677     invalidFeedbackClass : "glyphicon-warning-sign",
8678     
8679     /**
8680      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8681      */
8682     validFeedbackClass : "glyphicon-ok",
8683     
8684     /**
8685      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8686      */
8687     selectOnFocus : false,
8688     
8689      /**
8690      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8691      */
8692     maskRe : null,
8693        /**
8694      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8695      */
8696     vtype : null,
8697     
8698       /**
8699      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8700      */
8701     disableKeyFilter : false,
8702     
8703        /**
8704      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8705      */
8706     disabled : false,
8707      /**
8708      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8709      */
8710     allowBlank : true,
8711     /**
8712      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8713      */
8714     blankText : "Please complete this mandatory field",
8715     
8716      /**
8717      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8718      */
8719     minLength : 0,
8720     /**
8721      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8722      */
8723     maxLength : Number.MAX_VALUE,
8724     /**
8725      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8726      */
8727     minLengthText : "The minimum length for this field is {0}",
8728     /**
8729      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8730      */
8731     maxLengthText : "The maximum length for this field is {0}",
8732   
8733     
8734     /**
8735      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8736      * If available, this function will be called only after the basic validators all return true, and will be passed the
8737      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8738      */
8739     validator : null,
8740     /**
8741      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8742      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8743      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8744      */
8745     regex : null,
8746     /**
8747      * @cfg {String} regexText -- Depricated - use Invalid Text
8748      */
8749     regexText : "",
8750     
8751     /**
8752      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8753      */
8754     invalidText : "",
8755     
8756     
8757     
8758     autocomplete: false,
8759     
8760     
8761     fieldLabel : '',
8762     inputType : 'text',
8763     
8764     name : false,
8765     placeholder: false,
8766     before : false,
8767     after : false,
8768     size : false,
8769     hasFocus : false,
8770     preventMark: false,
8771     isFormField : true,
8772     value : '',
8773     labelWidth : 2,
8774     labelAlign : false,
8775     readOnly : false,
8776     align : false,
8777     formatedValue : false,
8778     forceFeedback : false,
8779     
8780     indicatorpos : 'left',
8781     
8782     labellg : 0,
8783     labelmd : 0,
8784     labelsm : 0,
8785     labelxs : 0,
8786     
8787     capture : '',
8788     accept : '',
8789     
8790     parentLabelAlign : function()
8791     {
8792         var parent = this;
8793         while (parent.parent()) {
8794             parent = parent.parent();
8795             if (typeof(parent.labelAlign) !='undefined') {
8796                 return parent.labelAlign;
8797             }
8798         }
8799         return 'left';
8800         
8801     },
8802     
8803     getAutoCreate : function()
8804     {
8805         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8806         
8807         var id = Roo.id();
8808         
8809         var cfg = {};
8810         
8811         if(this.inputType != 'hidden'){
8812             cfg.cls = 'form-group' //input-group
8813         }
8814         
8815         var input =  {
8816             tag: 'input',
8817             id : id,
8818             type : this.inputType,
8819             value : this.value,
8820             cls : 'form-control',
8821             placeholder : this.placeholder || '',
8822             autocomplete : this.autocomplete || 'new-password'
8823         };
8824         
8825         if(this.capture.length){
8826             input.capture = this.capture;
8827         }
8828         
8829         if(this.accept.length){
8830             input.accept = this.accept + "/*";
8831         }
8832         
8833         if(this.align){
8834             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8835         }
8836         
8837         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8838             input.maxLength = this.maxLength;
8839         }
8840         
8841         if (this.disabled) {
8842             input.disabled=true;
8843         }
8844         
8845         if (this.readOnly) {
8846             input.readonly=true;
8847         }
8848         
8849         if (this.name) {
8850             input.name = this.name;
8851         }
8852         
8853         if (this.size) {
8854             input.cls += ' input-' + this.size;
8855         }
8856         
8857         var settings=this;
8858         ['xs','sm','md','lg'].map(function(size){
8859             if (settings[size]) {
8860                 cfg.cls += ' col-' + size + '-' + settings[size];
8861             }
8862         });
8863         
8864         var inputblock = input;
8865         
8866         var feedback = {
8867             tag: 'span',
8868             cls: 'glyphicon form-control-feedback'
8869         };
8870             
8871         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8872             
8873             inputblock = {
8874                 cls : 'has-feedback',
8875                 cn :  [
8876                     input,
8877                     feedback
8878                 ] 
8879             };  
8880         }
8881         
8882         if (this.before || this.after) {
8883             
8884             inputblock = {
8885                 cls : 'input-group',
8886                 cn :  [] 
8887             };
8888             
8889             if (this.before && typeof(this.before) == 'string') {
8890                 
8891                 inputblock.cn.push({
8892                     tag :'span',
8893                     cls : 'roo-input-before input-group-addon',
8894                     html : this.before
8895                 });
8896             }
8897             if (this.before && typeof(this.before) == 'object') {
8898                 this.before = Roo.factory(this.before);
8899                 
8900                 inputblock.cn.push({
8901                     tag :'span',
8902                     cls : 'roo-input-before input-group-' +
8903                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8904                 });
8905             }
8906             
8907             inputblock.cn.push(input);
8908             
8909             if (this.after && typeof(this.after) == 'string') {
8910                 inputblock.cn.push({
8911                     tag :'span',
8912                     cls : 'roo-input-after input-group-addon',
8913                     html : this.after
8914                 });
8915             }
8916             if (this.after && typeof(this.after) == 'object') {
8917                 this.after = Roo.factory(this.after);
8918                 
8919                 inputblock.cn.push({
8920                     tag :'span',
8921                     cls : 'roo-input-after input-group-' +
8922                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8923                 });
8924             }
8925             
8926             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8927                 inputblock.cls += ' has-feedback';
8928                 inputblock.cn.push(feedback);
8929             }
8930         };
8931         
8932         if (align ==='left' && this.fieldLabel.length) {
8933             
8934             cfg.cls += ' roo-form-group-label-left';
8935             
8936             cfg.cn = [
8937                 {
8938                     tag : 'i',
8939                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8940                     tooltip : 'This field is required'
8941                 },
8942                 {
8943                     tag: 'label',
8944                     'for' :  id,
8945                     cls : 'control-label',
8946                     html : this.fieldLabel
8947
8948                 },
8949                 {
8950                     cls : "", 
8951                     cn: [
8952                         inputblock
8953                     ]
8954                 }
8955             ];
8956             
8957             var labelCfg = cfg.cn[1];
8958             var contentCfg = cfg.cn[2];
8959             
8960             if(this.indicatorpos == 'right'){
8961                 cfg.cn = [
8962                     {
8963                         tag: 'label',
8964                         'for' :  id,
8965                         cls : 'control-label',
8966                         cn : [
8967                             {
8968                                 tag : 'span',
8969                                 html : this.fieldLabel
8970                             },
8971                             {
8972                                 tag : 'i',
8973                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8974                                 tooltip : 'This field is required'
8975                             }
8976                         ]
8977                     },
8978                     {
8979                         cls : "",
8980                         cn: [
8981                             inputblock
8982                         ]
8983                     }
8984
8985                 ];
8986                 
8987                 labelCfg = cfg.cn[0];
8988                 contentCfg = cfg.cn[1];
8989             
8990             }
8991             
8992             if(this.labelWidth > 12){
8993                 labelCfg.style = "width: " + this.labelWidth + 'px';
8994             }
8995             
8996             if(this.labelWidth < 13 && this.labelmd == 0){
8997                 this.labelmd = this.labelWidth;
8998             }
8999             
9000             if(this.labellg > 0){
9001                 labelCfg.cls += ' col-lg-' + this.labellg;
9002                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9003             }
9004             
9005             if(this.labelmd > 0){
9006                 labelCfg.cls += ' col-md-' + this.labelmd;
9007                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9008             }
9009             
9010             if(this.labelsm > 0){
9011                 labelCfg.cls += ' col-sm-' + this.labelsm;
9012                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9013             }
9014             
9015             if(this.labelxs > 0){
9016                 labelCfg.cls += ' col-xs-' + this.labelxs;
9017                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9018             }
9019             
9020             
9021         } else if ( this.fieldLabel.length) {
9022                 
9023             cfg.cn = [
9024                 {
9025                     tag : 'i',
9026                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9027                     tooltip : 'This field is required'
9028                 },
9029                 {
9030                     tag: 'label',
9031                    //cls : 'input-group-addon',
9032                     html : this.fieldLabel
9033
9034                 },
9035
9036                inputblock
9037
9038            ];
9039            
9040            if(this.indicatorpos == 'right'){
9041                 
9042                 cfg.cn = [
9043                     {
9044                         tag: 'label',
9045                        //cls : 'input-group-addon',
9046                         html : this.fieldLabel
9047
9048                     },
9049                     {
9050                         tag : 'i',
9051                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9052                         tooltip : 'This field is required'
9053                     },
9054
9055                    inputblock
9056
9057                ];
9058
9059             }
9060
9061         } else {
9062             
9063             cfg.cn = [
9064
9065                     inputblock
9066
9067             ];
9068                 
9069                 
9070         };
9071         
9072         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9073            cfg.cls += ' navbar-form';
9074         }
9075         
9076         if (this.parentType === 'NavGroup') {
9077            cfg.cls += ' navbar-form';
9078            cfg.tag = 'li';
9079         }
9080         
9081         return cfg;
9082         
9083     },
9084     /**
9085      * return the real input element.
9086      */
9087     inputEl: function ()
9088     {
9089         return this.el.select('input.form-control',true).first();
9090     },
9091     
9092     tooltipEl : function()
9093     {
9094         return this.inputEl();
9095     },
9096     
9097     indicatorEl : function()
9098     {
9099         var indicator = this.el.select('i.roo-required-indicator',true).first();
9100         
9101         if(!indicator){
9102             return false;
9103         }
9104         
9105         return indicator;
9106         
9107     },
9108     
9109     setDisabled : function(v)
9110     {
9111         var i  = this.inputEl().dom;
9112         if (!v) {
9113             i.removeAttribute('disabled');
9114             return;
9115             
9116         }
9117         i.setAttribute('disabled','true');
9118     },
9119     initEvents : function()
9120     {
9121           
9122         this.inputEl().on("keydown" , this.fireKey,  this);
9123         this.inputEl().on("focus", this.onFocus,  this);
9124         this.inputEl().on("blur", this.onBlur,  this);
9125         
9126         this.inputEl().relayEvent('keyup', this);
9127         
9128         this.indicator = this.indicatorEl();
9129         
9130         if(this.indicator){
9131             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9132         }
9133  
9134         // reference to original value for reset
9135         this.originalValue = this.getValue();
9136         //Roo.form.TextField.superclass.initEvents.call(this);
9137         if(this.validationEvent == 'keyup'){
9138             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9139             this.inputEl().on('keyup', this.filterValidation, this);
9140         }
9141         else if(this.validationEvent !== false){
9142             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9143         }
9144         
9145         if(this.selectOnFocus){
9146             this.on("focus", this.preFocus, this);
9147             
9148         }
9149         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9150             this.inputEl().on("keypress", this.filterKeys, this);
9151         } else {
9152             this.inputEl().relayEvent('keypress', this);
9153         }
9154        /* if(this.grow){
9155             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9156             this.el.on("click", this.autoSize,  this);
9157         }
9158         */
9159         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9160             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9161         }
9162         
9163         if (typeof(this.before) == 'object') {
9164             this.before.render(this.el.select('.roo-input-before',true).first());
9165         }
9166         if (typeof(this.after) == 'object') {
9167             this.after.render(this.el.select('.roo-input-after',true).first());
9168         }
9169         
9170         this.inputEl().on('change', this.onChange, this);
9171         
9172     },
9173     filterValidation : function(e){
9174         if(!e.isNavKeyPress()){
9175             this.validationTask.delay(this.validationDelay);
9176         }
9177     },
9178      /**
9179      * Validates the field value
9180      * @return {Boolean} True if the value is valid, else false
9181      */
9182     validate : function(){
9183         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9184         if(this.disabled || this.validateValue(this.getRawValue())){
9185             this.markValid();
9186             return true;
9187         }
9188         
9189         this.markInvalid();
9190         return false;
9191     },
9192     
9193     
9194     /**
9195      * Validates a value according to the field's validation rules and marks the field as invalid
9196      * if the validation fails
9197      * @param {Mixed} value The value to validate
9198      * @return {Boolean} True if the value is valid, else false
9199      */
9200     validateValue : function(value)
9201     {
9202         if(this.getVisibilityEl().hasClass('hidden')){
9203             return true;
9204         }
9205         
9206         if(value.length < 1)  { // if it's blank
9207             if(this.allowBlank){
9208                 return true;
9209             }
9210             return false;
9211         }
9212         
9213         if(value.length < this.minLength){
9214             return false;
9215         }
9216         if(value.length > this.maxLength){
9217             return false;
9218         }
9219         if(this.vtype){
9220             var vt = Roo.form.VTypes;
9221             if(!vt[this.vtype](value, this)){
9222                 return false;
9223             }
9224         }
9225         if(typeof this.validator == "function"){
9226             var msg = this.validator(value);
9227             if(msg !== true){
9228                 return false;
9229             }
9230             if (typeof(msg) == 'string') {
9231                 this.invalidText = msg;
9232             }
9233         }
9234         
9235         if(this.regex && !this.regex.test(value)){
9236             return false;
9237         }
9238         
9239         return true;
9240     },
9241     
9242      // private
9243     fireKey : function(e){
9244         //Roo.log('field ' + e.getKey());
9245         if(e.isNavKeyPress()){
9246             this.fireEvent("specialkey", this, e);
9247         }
9248     },
9249     focus : function (selectText){
9250         if(this.rendered){
9251             this.inputEl().focus();
9252             if(selectText === true){
9253                 this.inputEl().dom.select();
9254             }
9255         }
9256         return this;
9257     } ,
9258     
9259     onFocus : function(){
9260         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9261            // this.el.addClass(this.focusClass);
9262         }
9263         if(!this.hasFocus){
9264             this.hasFocus = true;
9265             this.startValue = this.getValue();
9266             this.fireEvent("focus", this);
9267         }
9268     },
9269     
9270     beforeBlur : Roo.emptyFn,
9271
9272     
9273     // private
9274     onBlur : function(){
9275         this.beforeBlur();
9276         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9277             //this.el.removeClass(this.focusClass);
9278         }
9279         this.hasFocus = false;
9280         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9281             this.validate();
9282         }
9283         var v = this.getValue();
9284         if(String(v) !== String(this.startValue)){
9285             this.fireEvent('change', this, v, this.startValue);
9286         }
9287         this.fireEvent("blur", this);
9288     },
9289     
9290     onChange : function(e)
9291     {
9292         var v = this.getValue();
9293         if(String(v) !== String(this.startValue)){
9294             this.fireEvent('change', this, v, this.startValue);
9295         }
9296         
9297     },
9298     
9299     /**
9300      * Resets the current field value to the originally loaded value and clears any validation messages
9301      */
9302     reset : function(){
9303         this.setValue(this.originalValue);
9304         this.validate();
9305     },
9306      /**
9307      * Returns the name of the field
9308      * @return {Mixed} name The name field
9309      */
9310     getName: function(){
9311         return this.name;
9312     },
9313      /**
9314      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9315      * @return {Mixed} value The field value
9316      */
9317     getValue : function(){
9318         
9319         var v = this.inputEl().getValue();
9320         
9321         return v;
9322     },
9323     /**
9324      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9325      * @return {Mixed} value The field value
9326      */
9327     getRawValue : function(){
9328         var v = this.inputEl().getValue();
9329         
9330         return v;
9331     },
9332     
9333     /**
9334      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9335      * @param {Mixed} value The value to set
9336      */
9337     setRawValue : function(v){
9338         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9339     },
9340     
9341     selectText : function(start, end){
9342         var v = this.getRawValue();
9343         if(v.length > 0){
9344             start = start === undefined ? 0 : start;
9345             end = end === undefined ? v.length : end;
9346             var d = this.inputEl().dom;
9347             if(d.setSelectionRange){
9348                 d.setSelectionRange(start, end);
9349             }else if(d.createTextRange){
9350                 var range = d.createTextRange();
9351                 range.moveStart("character", start);
9352                 range.moveEnd("character", v.length-end);
9353                 range.select();
9354             }
9355         }
9356     },
9357     
9358     /**
9359      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9360      * @param {Mixed} value The value to set
9361      */
9362     setValue : function(v){
9363         this.value = v;
9364         if(this.rendered){
9365             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9366             this.validate();
9367         }
9368     },
9369     
9370     /*
9371     processValue : function(value){
9372         if(this.stripCharsRe){
9373             var newValue = value.replace(this.stripCharsRe, '');
9374             if(newValue !== value){
9375                 this.setRawValue(newValue);
9376                 return newValue;
9377             }
9378         }
9379         return value;
9380     },
9381   */
9382     preFocus : function(){
9383         
9384         if(this.selectOnFocus){
9385             this.inputEl().dom.select();
9386         }
9387     },
9388     filterKeys : function(e){
9389         var k = e.getKey();
9390         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9391             return;
9392         }
9393         var c = e.getCharCode(), cc = String.fromCharCode(c);
9394         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9395             return;
9396         }
9397         if(!this.maskRe.test(cc)){
9398             e.stopEvent();
9399         }
9400     },
9401      /**
9402      * Clear any invalid styles/messages for this field
9403      */
9404     clearInvalid : function(){
9405         
9406         if(!this.el || this.preventMark){ // not rendered
9407             return;
9408         }
9409         
9410      
9411         this.el.removeClass(this.invalidClass);
9412         
9413         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9414             
9415             var feedback = this.el.select('.form-control-feedback', true).first();
9416             
9417             if(feedback){
9418                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9419             }
9420             
9421         }
9422         
9423         if(this.indicator){
9424             this.indicator.removeClass('visible');
9425             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9426         }
9427         
9428         this.fireEvent('valid', this);
9429     },
9430     
9431      /**
9432      * Mark this field as valid
9433      */
9434     markValid : function()
9435     {
9436         if(!this.el  || this.preventMark){ // not rendered...
9437             return;
9438         }
9439         
9440         this.el.removeClass([this.invalidClass, this.validClass]);
9441         
9442         var feedback = this.el.select('.form-control-feedback', true).first();
9443             
9444         if(feedback){
9445             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9446         }
9447         
9448         if(this.indicator){
9449             this.indicator.removeClass('visible');
9450             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9451         }
9452         
9453         if(this.disabled){
9454             return;
9455         }
9456         
9457         if(this.allowBlank && !this.getRawValue().length){
9458             return;
9459         }
9460         
9461         this.el.addClass(this.validClass);
9462         
9463         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9464             
9465             var feedback = this.el.select('.form-control-feedback', true).first();
9466             
9467             if(feedback){
9468                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9469                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9470             }
9471             
9472         }
9473         
9474         this.fireEvent('valid', this);
9475     },
9476     
9477      /**
9478      * Mark this field as invalid
9479      * @param {String} msg The validation message
9480      */
9481     markInvalid : function(msg)
9482     {
9483         if(!this.el  || this.preventMark){ // not rendered
9484             return;
9485         }
9486         
9487         this.el.removeClass([this.invalidClass, this.validClass]);
9488         
9489         var feedback = this.el.select('.form-control-feedback', true).first();
9490             
9491         if(feedback){
9492             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9493         }
9494
9495         if(this.disabled){
9496             return;
9497         }
9498         
9499         if(this.allowBlank && !this.getRawValue().length){
9500             return;
9501         }
9502         
9503         if(this.indicator){
9504             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9505             this.indicator.addClass('visible');
9506         }
9507         
9508         this.el.addClass(this.invalidClass);
9509         
9510         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9511             
9512             var feedback = this.el.select('.form-control-feedback', true).first();
9513             
9514             if(feedback){
9515                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9516                 
9517                 if(this.getValue().length || this.forceFeedback){
9518                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9519                 }
9520                 
9521             }
9522             
9523         }
9524         
9525         this.fireEvent('invalid', this, msg);
9526     },
9527     // private
9528     SafariOnKeyDown : function(event)
9529     {
9530         // this is a workaround for a password hang bug on chrome/ webkit.
9531         if (this.inputEl().dom.type != 'password') {
9532             return;
9533         }
9534         
9535         var isSelectAll = false;
9536         
9537         if(this.inputEl().dom.selectionEnd > 0){
9538             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9539         }
9540         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9541             event.preventDefault();
9542             this.setValue('');
9543             return;
9544         }
9545         
9546         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9547             
9548             event.preventDefault();
9549             // this is very hacky as keydown always get's upper case.
9550             //
9551             var cc = String.fromCharCode(event.getCharCode());
9552             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9553             
9554         }
9555     },
9556     adjustWidth : function(tag, w){
9557         tag = tag.toLowerCase();
9558         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9559             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9560                 if(tag == 'input'){
9561                     return w + 2;
9562                 }
9563                 if(tag == 'textarea'){
9564                     return w-2;
9565                 }
9566             }else if(Roo.isOpera){
9567                 if(tag == 'input'){
9568                     return w + 2;
9569                 }
9570                 if(tag == 'textarea'){
9571                     return w-2;
9572                 }
9573             }
9574         }
9575         return w;
9576     },
9577     
9578     setFieldLabel : function(v)
9579     {
9580         if(!this.rendered){
9581             return;
9582         }
9583         
9584         if(this.indicator){
9585             var ar = this.el.select('label > span',true);
9586             
9587             if (ar.elements.length) {
9588                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9589                 this.fieldLabel = v;
9590                 return;
9591             }
9592             
9593             var br = this.el.select('label',true);
9594             
9595             if(br.elements.length) {
9596                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9597                 this.fieldLabel = v;
9598                 return;
9599             }
9600             
9601             Roo.log('Cannot Found any of label > span || label in input');
9602             return;
9603         }
9604         
9605         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9606         this.fieldLabel = v;
9607         
9608         
9609     }
9610 });
9611
9612  
9613 /*
9614  * - LGPL
9615  *
9616  * Input
9617  * 
9618  */
9619
9620 /**
9621  * @class Roo.bootstrap.TextArea
9622  * @extends Roo.bootstrap.Input
9623  * Bootstrap TextArea class
9624  * @cfg {Number} cols Specifies the visible width of a text area
9625  * @cfg {Number} rows Specifies the visible number of lines in a text area
9626  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9627  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9628  * @cfg {string} html text
9629  * 
9630  * @constructor
9631  * Create a new TextArea
9632  * @param {Object} config The config object
9633  */
9634
9635 Roo.bootstrap.TextArea = function(config){
9636     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9637    
9638 };
9639
9640 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9641      
9642     cols : false,
9643     rows : 5,
9644     readOnly : false,
9645     warp : 'soft',
9646     resize : false,
9647     value: false,
9648     html: false,
9649     
9650     getAutoCreate : function(){
9651         
9652         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9653         
9654         var id = Roo.id();
9655         
9656         var cfg = {};
9657         
9658         if(this.inputType != 'hidden'){
9659             cfg.cls = 'form-group' //input-group
9660         }
9661         
9662         var input =  {
9663             tag: 'textarea',
9664             id : id,
9665             warp : this.warp,
9666             rows : this.rows,
9667             value : this.value || '',
9668             html: this.html || '',
9669             cls : 'form-control',
9670             placeholder : this.placeholder || '' 
9671             
9672         };
9673         
9674         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9675             input.maxLength = this.maxLength;
9676         }
9677         
9678         if(this.resize){
9679             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9680         }
9681         
9682         if(this.cols){
9683             input.cols = this.cols;
9684         }
9685         
9686         if (this.readOnly) {
9687             input.readonly = true;
9688         }
9689         
9690         if (this.name) {
9691             input.name = this.name;
9692         }
9693         
9694         if (this.size) {
9695             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9696         }
9697         
9698         var settings=this;
9699         ['xs','sm','md','lg'].map(function(size){
9700             if (settings[size]) {
9701                 cfg.cls += ' col-' + size + '-' + settings[size];
9702             }
9703         });
9704         
9705         var inputblock = input;
9706         
9707         if(this.hasFeedback && !this.allowBlank){
9708             
9709             var feedback = {
9710                 tag: 'span',
9711                 cls: 'glyphicon form-control-feedback'
9712             };
9713
9714             inputblock = {
9715                 cls : 'has-feedback',
9716                 cn :  [
9717                     input,
9718                     feedback
9719                 ] 
9720             };  
9721         }
9722         
9723         
9724         if (this.before || this.after) {
9725             
9726             inputblock = {
9727                 cls : 'input-group',
9728                 cn :  [] 
9729             };
9730             if (this.before) {
9731                 inputblock.cn.push({
9732                     tag :'span',
9733                     cls : 'input-group-addon',
9734                     html : this.before
9735                 });
9736             }
9737             
9738             inputblock.cn.push(input);
9739             
9740             if(this.hasFeedback && !this.allowBlank){
9741                 inputblock.cls += ' has-feedback';
9742                 inputblock.cn.push(feedback);
9743             }
9744             
9745             if (this.after) {
9746                 inputblock.cn.push({
9747                     tag :'span',
9748                     cls : 'input-group-addon',
9749                     html : this.after
9750                 });
9751             }
9752             
9753         }
9754         
9755         if (align ==='left' && this.fieldLabel.length) {
9756             cfg.cn = [
9757                 {
9758                     tag: 'label',
9759                     'for' :  id,
9760                     cls : 'control-label',
9761                     html : this.fieldLabel
9762                 },
9763                 {
9764                     cls : "",
9765                     cn: [
9766                         inputblock
9767                     ]
9768                 }
9769
9770             ];
9771             
9772             if(this.labelWidth > 12){
9773                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9774             }
9775
9776             if(this.labelWidth < 13 && this.labelmd == 0){
9777                 this.labelmd = this.labelWidth;
9778             }
9779
9780             if(this.labellg > 0){
9781                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9782                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9783             }
9784
9785             if(this.labelmd > 0){
9786                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9787                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9788             }
9789
9790             if(this.labelsm > 0){
9791                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9792                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9793             }
9794
9795             if(this.labelxs > 0){
9796                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9797                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9798             }
9799             
9800         } else if ( this.fieldLabel.length) {
9801             cfg.cn = [
9802
9803                {
9804                    tag: 'label',
9805                    //cls : 'input-group-addon',
9806                    html : this.fieldLabel
9807
9808                },
9809
9810                inputblock
9811
9812            ];
9813
9814         } else {
9815
9816             cfg.cn = [
9817
9818                 inputblock
9819
9820             ];
9821                 
9822         }
9823         
9824         if (this.disabled) {
9825             input.disabled=true;
9826         }
9827         
9828         return cfg;
9829         
9830     },
9831     /**
9832      * return the real textarea element.
9833      */
9834     inputEl: function ()
9835     {
9836         return this.el.select('textarea.form-control',true).first();
9837     },
9838     
9839     /**
9840      * Clear any invalid styles/messages for this field
9841      */
9842     clearInvalid : function()
9843     {
9844         
9845         if(!this.el || this.preventMark){ // not rendered
9846             return;
9847         }
9848         
9849         var label = this.el.select('label', true).first();
9850         var icon = this.el.select('i.fa-star', true).first();
9851         
9852         if(label && icon){
9853             icon.remove();
9854         }
9855         
9856         this.el.removeClass(this.invalidClass);
9857         
9858         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9859             
9860             var feedback = this.el.select('.form-control-feedback', true).first();
9861             
9862             if(feedback){
9863                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9864             }
9865             
9866         }
9867         
9868         this.fireEvent('valid', this);
9869     },
9870     
9871      /**
9872      * Mark this field as valid
9873      */
9874     markValid : function()
9875     {
9876         if(!this.el  || this.preventMark){ // not rendered
9877             return;
9878         }
9879         
9880         this.el.removeClass([this.invalidClass, this.validClass]);
9881         
9882         var feedback = this.el.select('.form-control-feedback', true).first();
9883             
9884         if(feedback){
9885             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9886         }
9887
9888         if(this.disabled || this.allowBlank){
9889             return;
9890         }
9891         
9892         var label = this.el.select('label', true).first();
9893         var icon = this.el.select('i.fa-star', true).first();
9894         
9895         if(label && icon){
9896             icon.remove();
9897         }
9898         
9899         this.el.addClass(this.validClass);
9900         
9901         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9902             
9903             var feedback = this.el.select('.form-control-feedback', true).first();
9904             
9905             if(feedback){
9906                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9907                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9908             }
9909             
9910         }
9911         
9912         this.fireEvent('valid', this);
9913     },
9914     
9915      /**
9916      * Mark this field as invalid
9917      * @param {String} msg The validation message
9918      */
9919     markInvalid : function(msg)
9920     {
9921         if(!this.el  || this.preventMark){ // not rendered
9922             return;
9923         }
9924         
9925         this.el.removeClass([this.invalidClass, this.validClass]);
9926         
9927         var feedback = this.el.select('.form-control-feedback', true).first();
9928             
9929         if(feedback){
9930             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9931         }
9932
9933         if(this.disabled || this.allowBlank){
9934             return;
9935         }
9936         
9937         var label = this.el.select('label', true).first();
9938         var icon = this.el.select('i.fa-star', true).first();
9939         
9940         if(!this.getValue().length && label && !icon){
9941             this.el.createChild({
9942                 tag : 'i',
9943                 cls : 'text-danger fa fa-lg fa-star',
9944                 tooltip : 'This field is required',
9945                 style : 'margin-right:5px;'
9946             }, label, true);
9947         }
9948
9949         this.el.addClass(this.invalidClass);
9950         
9951         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9952             
9953             var feedback = this.el.select('.form-control-feedback', true).first();
9954             
9955             if(feedback){
9956                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9957                 
9958                 if(this.getValue().length || this.forceFeedback){
9959                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9960                 }
9961                 
9962             }
9963             
9964         }
9965         
9966         this.fireEvent('invalid', this, msg);
9967     }
9968 });
9969
9970  
9971 /*
9972  * - LGPL
9973  *
9974  * trigger field - base class for combo..
9975  * 
9976  */
9977  
9978 /**
9979  * @class Roo.bootstrap.TriggerField
9980  * @extends Roo.bootstrap.Input
9981  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9982  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9983  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9984  * for which you can provide a custom implementation.  For example:
9985  * <pre><code>
9986 var trigger = new Roo.bootstrap.TriggerField();
9987 trigger.onTriggerClick = myTriggerFn;
9988 trigger.applyTo('my-field');
9989 </code></pre>
9990  *
9991  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9992  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9993  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9994  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9995  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9996
9997  * @constructor
9998  * Create a new TriggerField.
9999  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10000  * to the base TextField)
10001  */
10002 Roo.bootstrap.TriggerField = function(config){
10003     this.mimicing = false;
10004     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10005 };
10006
10007 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10008     /**
10009      * @cfg {String} triggerClass A CSS class to apply to the trigger
10010      */
10011      /**
10012      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10013      */
10014     hideTrigger:false,
10015
10016     /**
10017      * @cfg {Boolean} removable (true|false) special filter default false
10018      */
10019     removable : false,
10020     
10021     /** @cfg {Boolean} grow @hide */
10022     /** @cfg {Number} growMin @hide */
10023     /** @cfg {Number} growMax @hide */
10024
10025     /**
10026      * @hide 
10027      * @method
10028      */
10029     autoSize: Roo.emptyFn,
10030     // private
10031     monitorTab : true,
10032     // private
10033     deferHeight : true,
10034
10035     
10036     actionMode : 'wrap',
10037     
10038     caret : false,
10039     
10040     
10041     getAutoCreate : function(){
10042        
10043         var align = this.labelAlign || this.parentLabelAlign();
10044         
10045         var id = Roo.id();
10046         
10047         var cfg = {
10048             cls: 'form-group' //input-group
10049         };
10050         
10051         
10052         var input =  {
10053             tag: 'input',
10054             id : id,
10055             type : this.inputType,
10056             cls : 'form-control',
10057             autocomplete: 'new-password',
10058             placeholder : this.placeholder || '' 
10059             
10060         };
10061         if (this.name) {
10062             input.name = this.name;
10063         }
10064         if (this.size) {
10065             input.cls += ' input-' + this.size;
10066         }
10067         
10068         if (this.disabled) {
10069             input.disabled=true;
10070         }
10071         
10072         var inputblock = input;
10073         
10074         if(this.hasFeedback && !this.allowBlank){
10075             
10076             var feedback = {
10077                 tag: 'span',
10078                 cls: 'glyphicon form-control-feedback'
10079             };
10080             
10081             if(this.removable && !this.editable && !this.tickable){
10082                 inputblock = {
10083                     cls : 'has-feedback',
10084                     cn :  [
10085                         inputblock,
10086                         {
10087                             tag: 'button',
10088                             html : 'x',
10089                             cls : 'roo-combo-removable-btn close'
10090                         },
10091                         feedback
10092                     ] 
10093                 };
10094             } else {
10095                 inputblock = {
10096                     cls : 'has-feedback',
10097                     cn :  [
10098                         inputblock,
10099                         feedback
10100                     ] 
10101                 };
10102             }
10103
10104         } else {
10105             if(this.removable && !this.editable && !this.tickable){
10106                 inputblock = {
10107                     cls : 'roo-removable',
10108                     cn :  [
10109                         inputblock,
10110                         {
10111                             tag: 'button',
10112                             html : 'x',
10113                             cls : 'roo-combo-removable-btn close'
10114                         }
10115                     ] 
10116                 };
10117             }
10118         }
10119         
10120         if (this.before || this.after) {
10121             
10122             inputblock = {
10123                 cls : 'input-group',
10124                 cn :  [] 
10125             };
10126             if (this.before) {
10127                 inputblock.cn.push({
10128                     tag :'span',
10129                     cls : 'input-group-addon',
10130                     html : this.before
10131                 });
10132             }
10133             
10134             inputblock.cn.push(input);
10135             
10136             if(this.hasFeedback && !this.allowBlank){
10137                 inputblock.cls += ' has-feedback';
10138                 inputblock.cn.push(feedback);
10139             }
10140             
10141             if (this.after) {
10142                 inputblock.cn.push({
10143                     tag :'span',
10144                     cls : 'input-group-addon',
10145                     html : this.after
10146                 });
10147             }
10148             
10149         };
10150         
10151         var box = {
10152             tag: 'div',
10153             cn: [
10154                 {
10155                     tag: 'input',
10156                     type : 'hidden',
10157                     cls: 'form-hidden-field'
10158                 },
10159                 inputblock
10160             ]
10161             
10162         };
10163         
10164         if(this.multiple){
10165             box = {
10166                 tag: 'div',
10167                 cn: [
10168                     {
10169                         tag: 'input',
10170                         type : 'hidden',
10171                         cls: 'form-hidden-field'
10172                     },
10173                     {
10174                         tag: 'ul',
10175                         cls: 'roo-select2-choices',
10176                         cn:[
10177                             {
10178                                 tag: 'li',
10179                                 cls: 'roo-select2-search-field',
10180                                 cn: [
10181
10182                                     inputblock
10183                                 ]
10184                             }
10185                         ]
10186                     }
10187                 ]
10188             }
10189         };
10190         
10191         var combobox = {
10192             cls: 'roo-select2-container input-group',
10193             cn: [
10194                 box
10195 //                {
10196 //                    tag: 'ul',
10197 //                    cls: 'typeahead typeahead-long dropdown-menu',
10198 //                    style: 'display:none'
10199 //                }
10200             ]
10201         };
10202         
10203         if(!this.multiple && this.showToggleBtn){
10204             
10205             var caret = {
10206                         tag: 'span',
10207                         cls: 'caret'
10208              };
10209             if (this.caret != false) {
10210                 caret = {
10211                      tag: 'i',
10212                      cls: 'fa fa-' + this.caret
10213                 };
10214                 
10215             }
10216             
10217             combobox.cn.push({
10218                 tag :'span',
10219                 cls : 'input-group-addon btn dropdown-toggle',
10220                 cn : [
10221                     caret,
10222                     {
10223                         tag: 'span',
10224                         cls: 'combobox-clear',
10225                         cn  : [
10226                             {
10227                                 tag : 'i',
10228                                 cls: 'icon-remove'
10229                             }
10230                         ]
10231                     }
10232                 ]
10233
10234             })
10235         }
10236         
10237         if(this.multiple){
10238             combobox.cls += ' roo-select2-container-multi';
10239         }
10240         
10241         if (align ==='left' && this.fieldLabel.length) {
10242             
10243             cfg.cls += ' roo-form-group-label-left';
10244
10245             cfg.cn = [
10246                 {
10247                     tag : 'i',
10248                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10249                     tooltip : 'This field is required'
10250                 },
10251                 {
10252                     tag: 'label',
10253                     'for' :  id,
10254                     cls : 'control-label',
10255                     html : this.fieldLabel
10256
10257                 },
10258                 {
10259                     cls : "", 
10260                     cn: [
10261                         combobox
10262                     ]
10263                 }
10264
10265             ];
10266             
10267             var labelCfg = cfg.cn[1];
10268             var contentCfg = cfg.cn[2];
10269             
10270             if(this.indicatorpos == 'right'){
10271                 cfg.cn = [
10272                     {
10273                         tag: 'label',
10274                         'for' :  id,
10275                         cls : 'control-label',
10276                         cn : [
10277                             {
10278                                 tag : 'span',
10279                                 html : this.fieldLabel
10280                             },
10281                             {
10282                                 tag : 'i',
10283                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10284                                 tooltip : 'This field is required'
10285                             }
10286                         ]
10287                     },
10288                     {
10289                         cls : "", 
10290                         cn: [
10291                             combobox
10292                         ]
10293                     }
10294
10295                 ];
10296                 
10297                 labelCfg = cfg.cn[0];
10298                 contentCfg = cfg.cn[1];
10299             }
10300             
10301             if(this.labelWidth > 12){
10302                 labelCfg.style = "width: " + this.labelWidth + 'px';
10303             }
10304             
10305             if(this.labelWidth < 13 && this.labelmd == 0){
10306                 this.labelmd = this.labelWidth;
10307             }
10308             
10309             if(this.labellg > 0){
10310                 labelCfg.cls += ' col-lg-' + this.labellg;
10311                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10312             }
10313             
10314             if(this.labelmd > 0){
10315                 labelCfg.cls += ' col-md-' + this.labelmd;
10316                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10317             }
10318             
10319             if(this.labelsm > 0){
10320                 labelCfg.cls += ' col-sm-' + this.labelsm;
10321                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10322             }
10323             
10324             if(this.labelxs > 0){
10325                 labelCfg.cls += ' col-xs-' + this.labelxs;
10326                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10327             }
10328             
10329         } else if ( this.fieldLabel.length) {
10330 //                Roo.log(" label");
10331             cfg.cn = [
10332                 {
10333                    tag : 'i',
10334                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10335                    tooltip : 'This field is required'
10336                },
10337                {
10338                    tag: 'label',
10339                    //cls : 'input-group-addon',
10340                    html : this.fieldLabel
10341
10342                },
10343
10344                combobox
10345
10346             ];
10347             
10348             if(this.indicatorpos == 'right'){
10349                 
10350                 cfg.cn = [
10351                     {
10352                        tag: 'label',
10353                        cn : [
10354                            {
10355                                tag : 'span',
10356                                html : this.fieldLabel
10357                            },
10358                            {
10359                               tag : 'i',
10360                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10361                               tooltip : 'This field is required'
10362                            }
10363                        ]
10364
10365                     },
10366                     combobox
10367
10368                 ];
10369
10370             }
10371
10372         } else {
10373             
10374 //                Roo.log(" no label && no align");
10375                 cfg = combobox
10376                      
10377                 
10378         }
10379         
10380         var settings=this;
10381         ['xs','sm','md','lg'].map(function(size){
10382             if (settings[size]) {
10383                 cfg.cls += ' col-' + size + '-' + settings[size];
10384             }
10385         });
10386         
10387         return cfg;
10388         
10389     },
10390     
10391     
10392     
10393     // private
10394     onResize : function(w, h){
10395 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10396 //        if(typeof w == 'number'){
10397 //            var x = w - this.trigger.getWidth();
10398 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10399 //            this.trigger.setStyle('left', x+'px');
10400 //        }
10401     },
10402
10403     // private
10404     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10405
10406     // private
10407     getResizeEl : function(){
10408         return this.inputEl();
10409     },
10410
10411     // private
10412     getPositionEl : function(){
10413         return this.inputEl();
10414     },
10415
10416     // private
10417     alignErrorIcon : function(){
10418         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10419     },
10420
10421     // private
10422     initEvents : function(){
10423         
10424         this.createList();
10425         
10426         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10427         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10428         if(!this.multiple && this.showToggleBtn){
10429             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10430             if(this.hideTrigger){
10431                 this.trigger.setDisplayed(false);
10432             }
10433             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10434         }
10435         
10436         if(this.multiple){
10437             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10438         }
10439         
10440         if(this.removable && !this.editable && !this.tickable){
10441             var close = this.closeTriggerEl();
10442             
10443             if(close){
10444                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10445                 close.on('click', this.removeBtnClick, this, close);
10446             }
10447         }
10448         
10449         //this.trigger.addClassOnOver('x-form-trigger-over');
10450         //this.trigger.addClassOnClick('x-form-trigger-click');
10451         
10452         //if(!this.width){
10453         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10454         //}
10455     },
10456     
10457     closeTriggerEl : function()
10458     {
10459         var close = this.el.select('.roo-combo-removable-btn', true).first();
10460         return close ? close : false;
10461     },
10462     
10463     removeBtnClick : function(e, h, el)
10464     {
10465         e.preventDefault();
10466         
10467         if(this.fireEvent("remove", this) !== false){
10468             this.reset();
10469             this.fireEvent("afterremove", this)
10470         }
10471     },
10472     
10473     createList : function()
10474     {
10475         this.list = Roo.get(document.body).createChild({
10476             tag: 'ul',
10477             cls: 'typeahead typeahead-long dropdown-menu',
10478             style: 'display:none'
10479         });
10480         
10481         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10482         
10483     },
10484
10485     // private
10486     initTrigger : function(){
10487        
10488     },
10489
10490     // private
10491     onDestroy : function(){
10492         if(this.trigger){
10493             this.trigger.removeAllListeners();
10494           //  this.trigger.remove();
10495         }
10496         //if(this.wrap){
10497         //    this.wrap.remove();
10498         //}
10499         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10500     },
10501
10502     // private
10503     onFocus : function(){
10504         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10505         /*
10506         if(!this.mimicing){
10507             this.wrap.addClass('x-trigger-wrap-focus');
10508             this.mimicing = true;
10509             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10510             if(this.monitorTab){
10511                 this.el.on("keydown", this.checkTab, this);
10512             }
10513         }
10514         */
10515     },
10516
10517     // private
10518     checkTab : function(e){
10519         if(e.getKey() == e.TAB){
10520             this.triggerBlur();
10521         }
10522     },
10523
10524     // private
10525     onBlur : function(){
10526         // do nothing
10527     },
10528
10529     // private
10530     mimicBlur : function(e, t){
10531         /*
10532         if(!this.wrap.contains(t) && this.validateBlur()){
10533             this.triggerBlur();
10534         }
10535         */
10536     },
10537
10538     // private
10539     triggerBlur : function(){
10540         this.mimicing = false;
10541         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10542         if(this.monitorTab){
10543             this.el.un("keydown", this.checkTab, this);
10544         }
10545         //this.wrap.removeClass('x-trigger-wrap-focus');
10546         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10547     },
10548
10549     // private
10550     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10551     validateBlur : function(e, t){
10552         return true;
10553     },
10554
10555     // private
10556     onDisable : function(){
10557         this.inputEl().dom.disabled = true;
10558         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10559         //if(this.wrap){
10560         //    this.wrap.addClass('x-item-disabled');
10561         //}
10562     },
10563
10564     // private
10565     onEnable : function(){
10566         this.inputEl().dom.disabled = false;
10567         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10568         //if(this.wrap){
10569         //    this.el.removeClass('x-item-disabled');
10570         //}
10571     },
10572
10573     // private
10574     onShow : function(){
10575         var ae = this.getActionEl();
10576         
10577         if(ae){
10578             ae.dom.style.display = '';
10579             ae.dom.style.visibility = 'visible';
10580         }
10581     },
10582
10583     // private
10584     
10585     onHide : function(){
10586         var ae = this.getActionEl();
10587         ae.dom.style.display = 'none';
10588     },
10589
10590     /**
10591      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10592      * by an implementing function.
10593      * @method
10594      * @param {EventObject} e
10595      */
10596     onTriggerClick : Roo.emptyFn
10597 });
10598  /*
10599  * Based on:
10600  * Ext JS Library 1.1.1
10601  * Copyright(c) 2006-2007, Ext JS, LLC.
10602  *
10603  * Originally Released Under LGPL - original licence link has changed is not relivant.
10604  *
10605  * Fork - LGPL
10606  * <script type="text/javascript">
10607  */
10608
10609
10610 /**
10611  * @class Roo.data.SortTypes
10612  * @singleton
10613  * Defines the default sorting (casting?) comparison functions used when sorting data.
10614  */
10615 Roo.data.SortTypes = {
10616     /**
10617      * Default sort that does nothing
10618      * @param {Mixed} s The value being converted
10619      * @return {Mixed} The comparison value
10620      */
10621     none : function(s){
10622         return s;
10623     },
10624     
10625     /**
10626      * The regular expression used to strip tags
10627      * @type {RegExp}
10628      * @property
10629      */
10630     stripTagsRE : /<\/?[^>]+>/gi,
10631     
10632     /**
10633      * Strips all HTML tags to sort on text only
10634      * @param {Mixed} s The value being converted
10635      * @return {String} The comparison value
10636      */
10637     asText : function(s){
10638         return String(s).replace(this.stripTagsRE, "");
10639     },
10640     
10641     /**
10642      * Strips all HTML tags to sort on text only - Case insensitive
10643      * @param {Mixed} s The value being converted
10644      * @return {String} The comparison value
10645      */
10646     asUCText : function(s){
10647         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10648     },
10649     
10650     /**
10651      * Case insensitive string
10652      * @param {Mixed} s The value being converted
10653      * @return {String} The comparison value
10654      */
10655     asUCString : function(s) {
10656         return String(s).toUpperCase();
10657     },
10658     
10659     /**
10660      * Date sorting
10661      * @param {Mixed} s The value being converted
10662      * @return {Number} The comparison value
10663      */
10664     asDate : function(s) {
10665         if(!s){
10666             return 0;
10667         }
10668         if(s instanceof Date){
10669             return s.getTime();
10670         }
10671         return Date.parse(String(s));
10672     },
10673     
10674     /**
10675      * Float sorting
10676      * @param {Mixed} s The value being converted
10677      * @return {Float} The comparison value
10678      */
10679     asFloat : function(s) {
10680         var val = parseFloat(String(s).replace(/,/g, ""));
10681         if(isNaN(val)) {
10682             val = 0;
10683         }
10684         return val;
10685     },
10686     
10687     /**
10688      * Integer sorting
10689      * @param {Mixed} s The value being converted
10690      * @return {Number} The comparison value
10691      */
10692     asInt : function(s) {
10693         var val = parseInt(String(s).replace(/,/g, ""));
10694         if(isNaN(val)) {
10695             val = 0;
10696         }
10697         return val;
10698     }
10699 };/*
10700  * Based on:
10701  * Ext JS Library 1.1.1
10702  * Copyright(c) 2006-2007, Ext JS, LLC.
10703  *
10704  * Originally Released Under LGPL - original licence link has changed is not relivant.
10705  *
10706  * Fork - LGPL
10707  * <script type="text/javascript">
10708  */
10709
10710 /**
10711 * @class Roo.data.Record
10712  * Instances of this class encapsulate both record <em>definition</em> information, and record
10713  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10714  * to access Records cached in an {@link Roo.data.Store} object.<br>
10715  * <p>
10716  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10717  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10718  * objects.<br>
10719  * <p>
10720  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10721  * @constructor
10722  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10723  * {@link #create}. The parameters are the same.
10724  * @param {Array} data An associative Array of data values keyed by the field name.
10725  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10726  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10727  * not specified an integer id is generated.
10728  */
10729 Roo.data.Record = function(data, id){
10730     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10731     this.data = data;
10732 };
10733
10734 /**
10735  * Generate a constructor for a specific record layout.
10736  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10737  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10738  * Each field definition object may contain the following properties: <ul>
10739  * <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,
10740  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10741  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10742  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10743  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10744  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10745  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10746  * this may be omitted.</p></li>
10747  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10748  * <ul><li>auto (Default, implies no conversion)</li>
10749  * <li>string</li>
10750  * <li>int</li>
10751  * <li>float</li>
10752  * <li>boolean</li>
10753  * <li>date</li></ul></p></li>
10754  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10755  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10756  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10757  * by the Reader into an object that will be stored in the Record. It is passed the
10758  * following parameters:<ul>
10759  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10760  * </ul></p></li>
10761  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10762  * </ul>
10763  * <br>usage:<br><pre><code>
10764 var TopicRecord = Roo.data.Record.create(
10765     {name: 'title', mapping: 'topic_title'},
10766     {name: 'author', mapping: 'username'},
10767     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10768     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10769     {name: 'lastPoster', mapping: 'user2'},
10770     {name: 'excerpt', mapping: 'post_text'}
10771 );
10772
10773 var myNewRecord = new TopicRecord({
10774     title: 'Do my job please',
10775     author: 'noobie',
10776     totalPosts: 1,
10777     lastPost: new Date(),
10778     lastPoster: 'Animal',
10779     excerpt: 'No way dude!'
10780 });
10781 myStore.add(myNewRecord);
10782 </code></pre>
10783  * @method create
10784  * @static
10785  */
10786 Roo.data.Record.create = function(o){
10787     var f = function(){
10788         f.superclass.constructor.apply(this, arguments);
10789     };
10790     Roo.extend(f, Roo.data.Record);
10791     var p = f.prototype;
10792     p.fields = new Roo.util.MixedCollection(false, function(field){
10793         return field.name;
10794     });
10795     for(var i = 0, len = o.length; i < len; i++){
10796         p.fields.add(new Roo.data.Field(o[i]));
10797     }
10798     f.getField = function(name){
10799         return p.fields.get(name);  
10800     };
10801     return f;
10802 };
10803
10804 Roo.data.Record.AUTO_ID = 1000;
10805 Roo.data.Record.EDIT = 'edit';
10806 Roo.data.Record.REJECT = 'reject';
10807 Roo.data.Record.COMMIT = 'commit';
10808
10809 Roo.data.Record.prototype = {
10810     /**
10811      * Readonly flag - true if this record has been modified.
10812      * @type Boolean
10813      */
10814     dirty : false,
10815     editing : false,
10816     error: null,
10817     modified: null,
10818
10819     // private
10820     join : function(store){
10821         this.store = store;
10822     },
10823
10824     /**
10825      * Set the named field to the specified value.
10826      * @param {String} name The name of the field to set.
10827      * @param {Object} value The value to set the field to.
10828      */
10829     set : function(name, value){
10830         if(this.data[name] == value){
10831             return;
10832         }
10833         this.dirty = true;
10834         if(!this.modified){
10835             this.modified = {};
10836         }
10837         if(typeof this.modified[name] == 'undefined'){
10838             this.modified[name] = this.data[name];
10839         }
10840         this.data[name] = value;
10841         if(!this.editing && this.store){
10842             this.store.afterEdit(this);
10843         }       
10844     },
10845
10846     /**
10847      * Get the value of the named field.
10848      * @param {String} name The name of the field to get the value of.
10849      * @return {Object} The value of the field.
10850      */
10851     get : function(name){
10852         return this.data[name]; 
10853     },
10854
10855     // private
10856     beginEdit : function(){
10857         this.editing = true;
10858         this.modified = {}; 
10859     },
10860
10861     // private
10862     cancelEdit : function(){
10863         this.editing = false;
10864         delete this.modified;
10865     },
10866
10867     // private
10868     endEdit : function(){
10869         this.editing = false;
10870         if(this.dirty && this.store){
10871             this.store.afterEdit(this);
10872         }
10873     },
10874
10875     /**
10876      * Usually called by the {@link Roo.data.Store} which owns the Record.
10877      * Rejects all changes made to the Record since either creation, or the last commit operation.
10878      * Modified fields are reverted to their original values.
10879      * <p>
10880      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10881      * of reject operations.
10882      */
10883     reject : function(){
10884         var m = this.modified;
10885         for(var n in m){
10886             if(typeof m[n] != "function"){
10887                 this.data[n] = m[n];
10888             }
10889         }
10890         this.dirty = false;
10891         delete this.modified;
10892         this.editing = false;
10893         if(this.store){
10894             this.store.afterReject(this);
10895         }
10896     },
10897
10898     /**
10899      * Usually called by the {@link Roo.data.Store} which owns the Record.
10900      * Commits all changes made to the Record since either creation, or the last commit operation.
10901      * <p>
10902      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10903      * of commit operations.
10904      */
10905     commit : function(){
10906         this.dirty = false;
10907         delete this.modified;
10908         this.editing = false;
10909         if(this.store){
10910             this.store.afterCommit(this);
10911         }
10912     },
10913
10914     // private
10915     hasError : function(){
10916         return this.error != null;
10917     },
10918
10919     // private
10920     clearError : function(){
10921         this.error = null;
10922     },
10923
10924     /**
10925      * Creates a copy of this record.
10926      * @param {String} id (optional) A new record id if you don't want to use this record's id
10927      * @return {Record}
10928      */
10929     copy : function(newId) {
10930         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10931     }
10932 };/*
10933  * Based on:
10934  * Ext JS Library 1.1.1
10935  * Copyright(c) 2006-2007, Ext JS, LLC.
10936  *
10937  * Originally Released Under LGPL - original licence link has changed is not relivant.
10938  *
10939  * Fork - LGPL
10940  * <script type="text/javascript">
10941  */
10942
10943
10944
10945 /**
10946  * @class Roo.data.Store
10947  * @extends Roo.util.Observable
10948  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10949  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10950  * <p>
10951  * 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
10952  * has no knowledge of the format of the data returned by the Proxy.<br>
10953  * <p>
10954  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10955  * instances from the data object. These records are cached and made available through accessor functions.
10956  * @constructor
10957  * Creates a new Store.
10958  * @param {Object} config A config object containing the objects needed for the Store to access data,
10959  * and read the data into Records.
10960  */
10961 Roo.data.Store = function(config){
10962     this.data = new Roo.util.MixedCollection(false);
10963     this.data.getKey = function(o){
10964         return o.id;
10965     };
10966     this.baseParams = {};
10967     // private
10968     this.paramNames = {
10969         "start" : "start",
10970         "limit" : "limit",
10971         "sort" : "sort",
10972         "dir" : "dir",
10973         "multisort" : "_multisort"
10974     };
10975
10976     if(config && config.data){
10977         this.inlineData = config.data;
10978         delete config.data;
10979     }
10980
10981     Roo.apply(this, config);
10982     
10983     if(this.reader){ // reader passed
10984         this.reader = Roo.factory(this.reader, Roo.data);
10985         this.reader.xmodule = this.xmodule || false;
10986         if(!this.recordType){
10987             this.recordType = this.reader.recordType;
10988         }
10989         if(this.reader.onMetaChange){
10990             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10991         }
10992     }
10993
10994     if(this.recordType){
10995         this.fields = this.recordType.prototype.fields;
10996     }
10997     this.modified = [];
10998
10999     this.addEvents({
11000         /**
11001          * @event datachanged
11002          * Fires when the data cache has changed, and a widget which is using this Store
11003          * as a Record cache should refresh its view.
11004          * @param {Store} this
11005          */
11006         datachanged : true,
11007         /**
11008          * @event metachange
11009          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11010          * @param {Store} this
11011          * @param {Object} meta The JSON metadata
11012          */
11013         metachange : true,
11014         /**
11015          * @event add
11016          * Fires when Records have been added to the Store
11017          * @param {Store} this
11018          * @param {Roo.data.Record[]} records The array of Records added
11019          * @param {Number} index The index at which the record(s) were added
11020          */
11021         add : true,
11022         /**
11023          * @event remove
11024          * Fires when a Record has been removed from the Store
11025          * @param {Store} this
11026          * @param {Roo.data.Record} record The Record that was removed
11027          * @param {Number} index The index at which the record was removed
11028          */
11029         remove : true,
11030         /**
11031          * @event update
11032          * Fires when a Record has been updated
11033          * @param {Store} this
11034          * @param {Roo.data.Record} record The Record that was updated
11035          * @param {String} operation The update operation being performed.  Value may be one of:
11036          * <pre><code>
11037  Roo.data.Record.EDIT
11038  Roo.data.Record.REJECT
11039  Roo.data.Record.COMMIT
11040          * </code></pre>
11041          */
11042         update : true,
11043         /**
11044          * @event clear
11045          * Fires when the data cache has been cleared.
11046          * @param {Store} this
11047          */
11048         clear : true,
11049         /**
11050          * @event beforeload
11051          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11052          * the load action will be canceled.
11053          * @param {Store} this
11054          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11055          */
11056         beforeload : true,
11057         /**
11058          * @event beforeloadadd
11059          * Fires after a new set of Records has been loaded.
11060          * @param {Store} this
11061          * @param {Roo.data.Record[]} records The Records that were loaded
11062          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11063          */
11064         beforeloadadd : true,
11065         /**
11066          * @event load
11067          * Fires after a new set of Records has been loaded, before they are added to the store.
11068          * @param {Store} this
11069          * @param {Roo.data.Record[]} records The Records that were loaded
11070          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11071          * @params {Object} return from reader
11072          */
11073         load : true,
11074         /**
11075          * @event loadexception
11076          * Fires if an exception occurs in the Proxy during loading.
11077          * Called with the signature of the Proxy's "loadexception" event.
11078          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11079          * 
11080          * @param {Proxy} 
11081          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11082          * @param {Object} load options 
11083          * @param {Object} jsonData from your request (normally this contains the Exception)
11084          */
11085         loadexception : true
11086     });
11087     
11088     if(this.proxy){
11089         this.proxy = Roo.factory(this.proxy, Roo.data);
11090         this.proxy.xmodule = this.xmodule || false;
11091         this.relayEvents(this.proxy,  ["loadexception"]);
11092     }
11093     this.sortToggle = {};
11094     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11095
11096     Roo.data.Store.superclass.constructor.call(this);
11097
11098     if(this.inlineData){
11099         this.loadData(this.inlineData);
11100         delete this.inlineData;
11101     }
11102 };
11103
11104 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11105      /**
11106     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11107     * without a remote query - used by combo/forms at present.
11108     */
11109     
11110     /**
11111     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11112     */
11113     /**
11114     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11115     */
11116     /**
11117     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11118     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11119     */
11120     /**
11121     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11122     * on any HTTP request
11123     */
11124     /**
11125     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11126     */
11127     /**
11128     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11129     */
11130     multiSort: false,
11131     /**
11132     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11133     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11134     */
11135     remoteSort : false,
11136
11137     /**
11138     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11139      * loaded or when a record is removed. (defaults to false).
11140     */
11141     pruneModifiedRecords : false,
11142
11143     // private
11144     lastOptions : null,
11145
11146     /**
11147      * Add Records to the Store and fires the add event.
11148      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11149      */
11150     add : function(records){
11151         records = [].concat(records);
11152         for(var i = 0, len = records.length; i < len; i++){
11153             records[i].join(this);
11154         }
11155         var index = this.data.length;
11156         this.data.addAll(records);
11157         this.fireEvent("add", this, records, index);
11158     },
11159
11160     /**
11161      * Remove a Record from the Store and fires the remove event.
11162      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11163      */
11164     remove : function(record){
11165         var index = this.data.indexOf(record);
11166         this.data.removeAt(index);
11167  
11168         if(this.pruneModifiedRecords){
11169             this.modified.remove(record);
11170         }
11171         this.fireEvent("remove", this, record, index);
11172     },
11173
11174     /**
11175      * Remove all Records from the Store and fires the clear event.
11176      */
11177     removeAll : function(){
11178         this.data.clear();
11179         if(this.pruneModifiedRecords){
11180             this.modified = [];
11181         }
11182         this.fireEvent("clear", this);
11183     },
11184
11185     /**
11186      * Inserts Records to the Store at the given index and fires the add event.
11187      * @param {Number} index The start index at which to insert the passed Records.
11188      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11189      */
11190     insert : function(index, records){
11191         records = [].concat(records);
11192         for(var i = 0, len = records.length; i < len; i++){
11193             this.data.insert(index, records[i]);
11194             records[i].join(this);
11195         }
11196         this.fireEvent("add", this, records, index);
11197     },
11198
11199     /**
11200      * Get the index within the cache of the passed Record.
11201      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11202      * @return {Number} The index of the passed Record. Returns -1 if not found.
11203      */
11204     indexOf : function(record){
11205         return this.data.indexOf(record);
11206     },
11207
11208     /**
11209      * Get the index within the cache of the Record with the passed id.
11210      * @param {String} id The id of the Record to find.
11211      * @return {Number} The index of the Record. Returns -1 if not found.
11212      */
11213     indexOfId : function(id){
11214         return this.data.indexOfKey(id);
11215     },
11216
11217     /**
11218      * Get the Record with the specified id.
11219      * @param {String} id The id of the Record to find.
11220      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11221      */
11222     getById : function(id){
11223         return this.data.key(id);
11224     },
11225
11226     /**
11227      * Get the Record at the specified index.
11228      * @param {Number} index The index of the Record to find.
11229      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11230      */
11231     getAt : function(index){
11232         return this.data.itemAt(index);
11233     },
11234
11235     /**
11236      * Returns a range of Records between specified indices.
11237      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11238      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11239      * @return {Roo.data.Record[]} An array of Records
11240      */
11241     getRange : function(start, end){
11242         return this.data.getRange(start, end);
11243     },
11244
11245     // private
11246     storeOptions : function(o){
11247         o = Roo.apply({}, o);
11248         delete o.callback;
11249         delete o.scope;
11250         this.lastOptions = o;
11251     },
11252
11253     /**
11254      * Loads the Record cache from the configured Proxy using the configured Reader.
11255      * <p>
11256      * If using remote paging, then the first load call must specify the <em>start</em>
11257      * and <em>limit</em> properties in the options.params property to establish the initial
11258      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11259      * <p>
11260      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11261      * and this call will return before the new data has been loaded. Perform any post-processing
11262      * in a callback function, or in a "load" event handler.</strong>
11263      * <p>
11264      * @param {Object} options An object containing properties which control loading options:<ul>
11265      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11266      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11267      * passed the following arguments:<ul>
11268      * <li>r : Roo.data.Record[]</li>
11269      * <li>options: Options object from the load call</li>
11270      * <li>success: Boolean success indicator</li></ul></li>
11271      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11272      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11273      * </ul>
11274      */
11275     load : function(options){
11276         options = options || {};
11277         if(this.fireEvent("beforeload", this, options) !== false){
11278             this.storeOptions(options);
11279             var p = Roo.apply(options.params || {}, this.baseParams);
11280             // if meta was not loaded from remote source.. try requesting it.
11281             if (!this.reader.metaFromRemote) {
11282                 p._requestMeta = 1;
11283             }
11284             if(this.sortInfo && this.remoteSort){
11285                 var pn = this.paramNames;
11286                 p[pn["sort"]] = this.sortInfo.field;
11287                 p[pn["dir"]] = this.sortInfo.direction;
11288             }
11289             if (this.multiSort) {
11290                 var pn = this.paramNames;
11291                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11292             }
11293             
11294             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11295         }
11296     },
11297
11298     /**
11299      * Reloads the Record cache from the configured Proxy using the configured Reader and
11300      * the options from the last load operation performed.
11301      * @param {Object} options (optional) An object containing properties which may override the options
11302      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11303      * the most recently used options are reused).
11304      */
11305     reload : function(options){
11306         this.load(Roo.applyIf(options||{}, this.lastOptions));
11307     },
11308
11309     // private
11310     // Called as a callback by the Reader during a load operation.
11311     loadRecords : function(o, options, success){
11312         if(!o || success === false){
11313             if(success !== false){
11314                 this.fireEvent("load", this, [], options, o);
11315             }
11316             if(options.callback){
11317                 options.callback.call(options.scope || this, [], options, false);
11318             }
11319             return;
11320         }
11321         // if data returned failure - throw an exception.
11322         if (o.success === false) {
11323             // show a message if no listener is registered.
11324             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11325                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11326             }
11327             // loadmask wil be hooked into this..
11328             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11329             return;
11330         }
11331         var r = o.records, t = o.totalRecords || r.length;
11332         
11333         this.fireEvent("beforeloadadd", this, r, options, o);
11334         
11335         if(!options || options.add !== true){
11336             if(this.pruneModifiedRecords){
11337                 this.modified = [];
11338             }
11339             for(var i = 0, len = r.length; i < len; i++){
11340                 r[i].join(this);
11341             }
11342             if(this.snapshot){
11343                 this.data = this.snapshot;
11344                 delete this.snapshot;
11345             }
11346             this.data.clear();
11347             this.data.addAll(r);
11348             this.totalLength = t;
11349             this.applySort();
11350             this.fireEvent("datachanged", this);
11351         }else{
11352             this.totalLength = Math.max(t, this.data.length+r.length);
11353             this.add(r);
11354         }
11355         
11356         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11357                 
11358             var e = new Roo.data.Record({});
11359
11360             e.set(this.parent.displayField, this.parent.emptyTitle);
11361             e.set(this.parent.valueField, '');
11362
11363             this.insert(0, e);
11364         }
11365             
11366         this.fireEvent("load", this, r, options, o);
11367         if(options.callback){
11368             options.callback.call(options.scope || this, r, options, true);
11369         }
11370     },
11371
11372
11373     /**
11374      * Loads data from a passed data block. A Reader which understands the format of the data
11375      * must have been configured in the constructor.
11376      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11377      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11378      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11379      */
11380     loadData : function(o, append){
11381         var r = this.reader.readRecords(o);
11382         this.loadRecords(r, {add: append}, true);
11383     },
11384
11385     /**
11386      * Gets the number of cached records.
11387      * <p>
11388      * <em>If using paging, this may not be the total size of the dataset. If the data object
11389      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11390      * the data set size</em>
11391      */
11392     getCount : function(){
11393         return this.data.length || 0;
11394     },
11395
11396     /**
11397      * Gets the total number of records in the dataset as returned by the server.
11398      * <p>
11399      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11400      * the dataset size</em>
11401      */
11402     getTotalCount : function(){
11403         return this.totalLength || 0;
11404     },
11405
11406     /**
11407      * Returns the sort state of the Store as an object with two properties:
11408      * <pre><code>
11409  field {String} The name of the field by which the Records are sorted
11410  direction {String} The sort order, "ASC" or "DESC"
11411      * </code></pre>
11412      */
11413     getSortState : function(){
11414         return this.sortInfo;
11415     },
11416
11417     // private
11418     applySort : function(){
11419         if(this.sortInfo && !this.remoteSort){
11420             var s = this.sortInfo, f = s.field;
11421             var st = this.fields.get(f).sortType;
11422             var fn = function(r1, r2){
11423                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11424                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11425             };
11426             this.data.sort(s.direction, fn);
11427             if(this.snapshot && this.snapshot != this.data){
11428                 this.snapshot.sort(s.direction, fn);
11429             }
11430         }
11431     },
11432
11433     /**
11434      * Sets the default sort column and order to be used by the next load operation.
11435      * @param {String} fieldName The name of the field to sort by.
11436      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11437      */
11438     setDefaultSort : function(field, dir){
11439         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11440     },
11441
11442     /**
11443      * Sort the Records.
11444      * If remote sorting is used, the sort is performed on the server, and the cache is
11445      * reloaded. If local sorting is used, the cache is sorted internally.
11446      * @param {String} fieldName The name of the field to sort by.
11447      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11448      */
11449     sort : function(fieldName, dir){
11450         var f = this.fields.get(fieldName);
11451         if(!dir){
11452             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11453             
11454             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11455                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11456             }else{
11457                 dir = f.sortDir;
11458             }
11459         }
11460         this.sortToggle[f.name] = dir;
11461         this.sortInfo = {field: f.name, direction: dir};
11462         if(!this.remoteSort){
11463             this.applySort();
11464             this.fireEvent("datachanged", this);
11465         }else{
11466             this.load(this.lastOptions);
11467         }
11468     },
11469
11470     /**
11471      * Calls the specified function for each of the Records in the cache.
11472      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11473      * Returning <em>false</em> aborts and exits the iteration.
11474      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11475      */
11476     each : function(fn, scope){
11477         this.data.each(fn, scope);
11478     },
11479
11480     /**
11481      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11482      * (e.g., during paging).
11483      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11484      */
11485     getModifiedRecords : function(){
11486         return this.modified;
11487     },
11488
11489     // private
11490     createFilterFn : function(property, value, anyMatch){
11491         if(!value.exec){ // not a regex
11492             value = String(value);
11493             if(value.length == 0){
11494                 return false;
11495             }
11496             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11497         }
11498         return function(r){
11499             return value.test(r.data[property]);
11500         };
11501     },
11502
11503     /**
11504      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11505      * @param {String} property A field on your records
11506      * @param {Number} start The record index to start at (defaults to 0)
11507      * @param {Number} end The last record index to include (defaults to length - 1)
11508      * @return {Number} The sum
11509      */
11510     sum : function(property, start, end){
11511         var rs = this.data.items, v = 0;
11512         start = start || 0;
11513         end = (end || end === 0) ? end : rs.length-1;
11514
11515         for(var i = start; i <= end; i++){
11516             v += (rs[i].data[property] || 0);
11517         }
11518         return v;
11519     },
11520
11521     /**
11522      * Filter the records by a specified property.
11523      * @param {String} field A field on your records
11524      * @param {String/RegExp} value Either a string that the field
11525      * should start with or a RegExp to test against the field
11526      * @param {Boolean} anyMatch True to match any part not just the beginning
11527      */
11528     filter : function(property, value, anyMatch){
11529         var fn = this.createFilterFn(property, value, anyMatch);
11530         return fn ? this.filterBy(fn) : this.clearFilter();
11531     },
11532
11533     /**
11534      * Filter by a function. The specified function will be called with each
11535      * record in this data source. If the function returns true the record is included,
11536      * otherwise it is filtered.
11537      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11538      * @param {Object} scope (optional) The scope of the function (defaults to this)
11539      */
11540     filterBy : function(fn, scope){
11541         this.snapshot = this.snapshot || this.data;
11542         this.data = this.queryBy(fn, scope||this);
11543         this.fireEvent("datachanged", this);
11544     },
11545
11546     /**
11547      * Query the records by a specified property.
11548      * @param {String} field A field on your records
11549      * @param {String/RegExp} value Either a string that the field
11550      * should start with or a RegExp to test against the field
11551      * @param {Boolean} anyMatch True to match any part not just the beginning
11552      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11553      */
11554     query : function(property, value, anyMatch){
11555         var fn = this.createFilterFn(property, value, anyMatch);
11556         return fn ? this.queryBy(fn) : this.data.clone();
11557     },
11558
11559     /**
11560      * Query by a function. The specified function will be called with each
11561      * record in this data source. If the function returns true the record is included
11562      * in the results.
11563      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11564      * @param {Object} scope (optional) The scope of the function (defaults to this)
11565       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11566      **/
11567     queryBy : function(fn, scope){
11568         var data = this.snapshot || this.data;
11569         return data.filterBy(fn, scope||this);
11570     },
11571
11572     /**
11573      * Collects unique values for a particular dataIndex from this store.
11574      * @param {String} dataIndex The property to collect
11575      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11576      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11577      * @return {Array} An array of the unique values
11578      **/
11579     collect : function(dataIndex, allowNull, bypassFilter){
11580         var d = (bypassFilter === true && this.snapshot) ?
11581                 this.snapshot.items : this.data.items;
11582         var v, sv, r = [], l = {};
11583         for(var i = 0, len = d.length; i < len; i++){
11584             v = d[i].data[dataIndex];
11585             sv = String(v);
11586             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11587                 l[sv] = true;
11588                 r[r.length] = v;
11589             }
11590         }
11591         return r;
11592     },
11593
11594     /**
11595      * Revert to a view of the Record cache with no filtering applied.
11596      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11597      */
11598     clearFilter : function(suppressEvent){
11599         if(this.snapshot && this.snapshot != this.data){
11600             this.data = this.snapshot;
11601             delete this.snapshot;
11602             if(suppressEvent !== true){
11603                 this.fireEvent("datachanged", this);
11604             }
11605         }
11606     },
11607
11608     // private
11609     afterEdit : function(record){
11610         if(this.modified.indexOf(record) == -1){
11611             this.modified.push(record);
11612         }
11613         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11614     },
11615     
11616     // private
11617     afterReject : function(record){
11618         this.modified.remove(record);
11619         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11620     },
11621
11622     // private
11623     afterCommit : function(record){
11624         this.modified.remove(record);
11625         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11626     },
11627
11628     /**
11629      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11630      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11631      */
11632     commitChanges : function(){
11633         var m = this.modified.slice(0);
11634         this.modified = [];
11635         for(var i = 0, len = m.length; i < len; i++){
11636             m[i].commit();
11637         }
11638     },
11639
11640     /**
11641      * Cancel outstanding changes on all changed records.
11642      */
11643     rejectChanges : function(){
11644         var m = this.modified.slice(0);
11645         this.modified = [];
11646         for(var i = 0, len = m.length; i < len; i++){
11647             m[i].reject();
11648         }
11649     },
11650
11651     onMetaChange : function(meta, rtype, o){
11652         this.recordType = rtype;
11653         this.fields = rtype.prototype.fields;
11654         delete this.snapshot;
11655         this.sortInfo = meta.sortInfo || this.sortInfo;
11656         this.modified = [];
11657         this.fireEvent('metachange', this, this.reader.meta);
11658     },
11659     
11660     moveIndex : function(data, type)
11661     {
11662         var index = this.indexOf(data);
11663         
11664         var newIndex = index + type;
11665         
11666         this.remove(data);
11667         
11668         this.insert(newIndex, data);
11669         
11670     }
11671 });/*
11672  * Based on:
11673  * Ext JS Library 1.1.1
11674  * Copyright(c) 2006-2007, Ext JS, LLC.
11675  *
11676  * Originally Released Under LGPL - original licence link has changed is not relivant.
11677  *
11678  * Fork - LGPL
11679  * <script type="text/javascript">
11680  */
11681
11682 /**
11683  * @class Roo.data.SimpleStore
11684  * @extends Roo.data.Store
11685  * Small helper class to make creating Stores from Array data easier.
11686  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11687  * @cfg {Array} fields An array of field definition objects, or field name strings.
11688  * @cfg {Array} data The multi-dimensional array of data
11689  * @constructor
11690  * @param {Object} config
11691  */
11692 Roo.data.SimpleStore = function(config){
11693     Roo.data.SimpleStore.superclass.constructor.call(this, {
11694         isLocal : true,
11695         reader: new Roo.data.ArrayReader({
11696                 id: config.id
11697             },
11698             Roo.data.Record.create(config.fields)
11699         ),
11700         proxy : new Roo.data.MemoryProxy(config.data)
11701     });
11702     this.load();
11703 };
11704 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11705  * Based on:
11706  * Ext JS Library 1.1.1
11707  * Copyright(c) 2006-2007, Ext JS, LLC.
11708  *
11709  * Originally Released Under LGPL - original licence link has changed is not relivant.
11710  *
11711  * Fork - LGPL
11712  * <script type="text/javascript">
11713  */
11714
11715 /**
11716 /**
11717  * @extends Roo.data.Store
11718  * @class Roo.data.JsonStore
11719  * Small helper class to make creating Stores for JSON data easier. <br/>
11720 <pre><code>
11721 var store = new Roo.data.JsonStore({
11722     url: 'get-images.php',
11723     root: 'images',
11724     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11725 });
11726 </code></pre>
11727  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11728  * JsonReader and HttpProxy (unless inline data is provided).</b>
11729  * @cfg {Array} fields An array of field definition objects, or field name strings.
11730  * @constructor
11731  * @param {Object} config
11732  */
11733 Roo.data.JsonStore = function(c){
11734     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11735         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11736         reader: new Roo.data.JsonReader(c, c.fields)
11737     }));
11738 };
11739 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11740  * Based on:
11741  * Ext JS Library 1.1.1
11742  * Copyright(c) 2006-2007, Ext JS, LLC.
11743  *
11744  * Originally Released Under LGPL - original licence link has changed is not relivant.
11745  *
11746  * Fork - LGPL
11747  * <script type="text/javascript">
11748  */
11749
11750  
11751 Roo.data.Field = function(config){
11752     if(typeof config == "string"){
11753         config = {name: config};
11754     }
11755     Roo.apply(this, config);
11756     
11757     if(!this.type){
11758         this.type = "auto";
11759     }
11760     
11761     var st = Roo.data.SortTypes;
11762     // named sortTypes are supported, here we look them up
11763     if(typeof this.sortType == "string"){
11764         this.sortType = st[this.sortType];
11765     }
11766     
11767     // set default sortType for strings and dates
11768     if(!this.sortType){
11769         switch(this.type){
11770             case "string":
11771                 this.sortType = st.asUCString;
11772                 break;
11773             case "date":
11774                 this.sortType = st.asDate;
11775                 break;
11776             default:
11777                 this.sortType = st.none;
11778         }
11779     }
11780
11781     // define once
11782     var stripRe = /[\$,%]/g;
11783
11784     // prebuilt conversion function for this field, instead of
11785     // switching every time we're reading a value
11786     if(!this.convert){
11787         var cv, dateFormat = this.dateFormat;
11788         switch(this.type){
11789             case "":
11790             case "auto":
11791             case undefined:
11792                 cv = function(v){ return v; };
11793                 break;
11794             case "string":
11795                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11796                 break;
11797             case "int":
11798                 cv = function(v){
11799                     return v !== undefined && v !== null && v !== '' ?
11800                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11801                     };
11802                 break;
11803             case "float":
11804                 cv = function(v){
11805                     return v !== undefined && v !== null && v !== '' ?
11806                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11807                     };
11808                 break;
11809             case "bool":
11810             case "boolean":
11811                 cv = function(v){ return v === true || v === "true" || v == 1; };
11812                 break;
11813             case "date":
11814                 cv = function(v){
11815                     if(!v){
11816                         return '';
11817                     }
11818                     if(v instanceof Date){
11819                         return v;
11820                     }
11821                     if(dateFormat){
11822                         if(dateFormat == "timestamp"){
11823                             return new Date(v*1000);
11824                         }
11825                         return Date.parseDate(v, dateFormat);
11826                     }
11827                     var parsed = Date.parse(v);
11828                     return parsed ? new Date(parsed) : null;
11829                 };
11830              break;
11831             
11832         }
11833         this.convert = cv;
11834     }
11835 };
11836
11837 Roo.data.Field.prototype = {
11838     dateFormat: null,
11839     defaultValue: "",
11840     mapping: null,
11841     sortType : null,
11842     sortDir : "ASC"
11843 };/*
11844  * Based on:
11845  * Ext JS Library 1.1.1
11846  * Copyright(c) 2006-2007, Ext JS, LLC.
11847  *
11848  * Originally Released Under LGPL - original licence link has changed is not relivant.
11849  *
11850  * Fork - LGPL
11851  * <script type="text/javascript">
11852  */
11853  
11854 // Base class for reading structured data from a data source.  This class is intended to be
11855 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11856
11857 /**
11858  * @class Roo.data.DataReader
11859  * Base class for reading structured data from a data source.  This class is intended to be
11860  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11861  */
11862
11863 Roo.data.DataReader = function(meta, recordType){
11864     
11865     this.meta = meta;
11866     
11867     this.recordType = recordType instanceof Array ? 
11868         Roo.data.Record.create(recordType) : recordType;
11869 };
11870
11871 Roo.data.DataReader.prototype = {
11872      /**
11873      * Create an empty record
11874      * @param {Object} data (optional) - overlay some values
11875      * @return {Roo.data.Record} record created.
11876      */
11877     newRow :  function(d) {
11878         var da =  {};
11879         this.recordType.prototype.fields.each(function(c) {
11880             switch( c.type) {
11881                 case 'int' : da[c.name] = 0; break;
11882                 case 'date' : da[c.name] = new Date(); break;
11883                 case 'float' : da[c.name] = 0.0; break;
11884                 case 'boolean' : da[c.name] = false; break;
11885                 default : da[c.name] = ""; break;
11886             }
11887             
11888         });
11889         return new this.recordType(Roo.apply(da, d));
11890     }
11891     
11892 };/*
11893  * Based on:
11894  * Ext JS Library 1.1.1
11895  * Copyright(c) 2006-2007, Ext JS, LLC.
11896  *
11897  * Originally Released Under LGPL - original licence link has changed is not relivant.
11898  *
11899  * Fork - LGPL
11900  * <script type="text/javascript">
11901  */
11902
11903 /**
11904  * @class Roo.data.DataProxy
11905  * @extends Roo.data.Observable
11906  * This class is an abstract base class for implementations which provide retrieval of
11907  * unformatted data objects.<br>
11908  * <p>
11909  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11910  * (of the appropriate type which knows how to parse the data object) to provide a block of
11911  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11912  * <p>
11913  * Custom implementations must implement the load method as described in
11914  * {@link Roo.data.HttpProxy#load}.
11915  */
11916 Roo.data.DataProxy = function(){
11917     this.addEvents({
11918         /**
11919          * @event beforeload
11920          * Fires before a network request is made to retrieve a data object.
11921          * @param {Object} This DataProxy object.
11922          * @param {Object} params The params parameter to the load function.
11923          */
11924         beforeload : true,
11925         /**
11926          * @event load
11927          * Fires before the load method's callback is called.
11928          * @param {Object} This DataProxy object.
11929          * @param {Object} o The data object.
11930          * @param {Object} arg The callback argument object passed to the load function.
11931          */
11932         load : true,
11933         /**
11934          * @event loadexception
11935          * Fires if an Exception occurs during data retrieval.
11936          * @param {Object} This DataProxy object.
11937          * @param {Object} o The data object.
11938          * @param {Object} arg The callback argument object passed to the load function.
11939          * @param {Object} e The Exception.
11940          */
11941         loadexception : true
11942     });
11943     Roo.data.DataProxy.superclass.constructor.call(this);
11944 };
11945
11946 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11947
11948     /**
11949      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11950      */
11951 /*
11952  * Based on:
11953  * Ext JS Library 1.1.1
11954  * Copyright(c) 2006-2007, Ext JS, LLC.
11955  *
11956  * Originally Released Under LGPL - original licence link has changed is not relivant.
11957  *
11958  * Fork - LGPL
11959  * <script type="text/javascript">
11960  */
11961 /**
11962  * @class Roo.data.MemoryProxy
11963  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11964  * to the Reader when its load method is called.
11965  * @constructor
11966  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11967  */
11968 Roo.data.MemoryProxy = function(data){
11969     if (data.data) {
11970         data = data.data;
11971     }
11972     Roo.data.MemoryProxy.superclass.constructor.call(this);
11973     this.data = data;
11974 };
11975
11976 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11977     
11978     /**
11979      * Load data from the requested source (in this case an in-memory
11980      * data object passed to the constructor), read the data object into
11981      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11982      * process that block using the passed callback.
11983      * @param {Object} params This parameter is not used by the MemoryProxy class.
11984      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11985      * object into a block of Roo.data.Records.
11986      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11987      * The function must be passed <ul>
11988      * <li>The Record block object</li>
11989      * <li>The "arg" argument from the load function</li>
11990      * <li>A boolean success indicator</li>
11991      * </ul>
11992      * @param {Object} scope The scope in which to call the callback
11993      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11994      */
11995     load : function(params, reader, callback, scope, arg){
11996         params = params || {};
11997         var result;
11998         try {
11999             result = reader.readRecords(this.data);
12000         }catch(e){
12001             this.fireEvent("loadexception", this, arg, null, e);
12002             callback.call(scope, null, arg, false);
12003             return;
12004         }
12005         callback.call(scope, result, arg, true);
12006     },
12007     
12008     // private
12009     update : function(params, records){
12010         
12011     }
12012 });/*
12013  * Based on:
12014  * Ext JS Library 1.1.1
12015  * Copyright(c) 2006-2007, Ext JS, LLC.
12016  *
12017  * Originally Released Under LGPL - original licence link has changed is not relivant.
12018  *
12019  * Fork - LGPL
12020  * <script type="text/javascript">
12021  */
12022 /**
12023  * @class Roo.data.HttpProxy
12024  * @extends Roo.data.DataProxy
12025  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12026  * configured to reference a certain URL.<br><br>
12027  * <p>
12028  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12029  * from which the running page was served.<br><br>
12030  * <p>
12031  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12032  * <p>
12033  * Be aware that to enable the browser to parse an XML document, the server must set
12034  * the Content-Type header in the HTTP response to "text/xml".
12035  * @constructor
12036  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12037  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12038  * will be used to make the request.
12039  */
12040 Roo.data.HttpProxy = function(conn){
12041     Roo.data.HttpProxy.superclass.constructor.call(this);
12042     // is conn a conn config or a real conn?
12043     this.conn = conn;
12044     this.useAjax = !conn || !conn.events;
12045   
12046 };
12047
12048 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12049     // thse are take from connection...
12050     
12051     /**
12052      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12053      */
12054     /**
12055      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12056      * extra parameters to each request made by this object. (defaults to undefined)
12057      */
12058     /**
12059      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12060      *  to each request made by this object. (defaults to undefined)
12061      */
12062     /**
12063      * @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)
12064      */
12065     /**
12066      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12067      */
12068      /**
12069      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12070      * @type Boolean
12071      */
12072   
12073
12074     /**
12075      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12076      * @type Boolean
12077      */
12078     /**
12079      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12080      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12081      * a finer-grained basis than the DataProxy events.
12082      */
12083     getConnection : function(){
12084         return this.useAjax ? Roo.Ajax : this.conn;
12085     },
12086
12087     /**
12088      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12089      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12090      * process that block using the passed callback.
12091      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12092      * for the request to the remote server.
12093      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12094      * object into a block of Roo.data.Records.
12095      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12096      * The function must be passed <ul>
12097      * <li>The Record block object</li>
12098      * <li>The "arg" argument from the load function</li>
12099      * <li>A boolean success indicator</li>
12100      * </ul>
12101      * @param {Object} scope The scope in which to call the callback
12102      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12103      */
12104     load : function(params, reader, callback, scope, arg){
12105         if(this.fireEvent("beforeload", this, params) !== false){
12106             var  o = {
12107                 params : params || {},
12108                 request: {
12109                     callback : callback,
12110                     scope : scope,
12111                     arg : arg
12112                 },
12113                 reader: reader,
12114                 callback : this.loadResponse,
12115                 scope: this
12116             };
12117             if(this.useAjax){
12118                 Roo.applyIf(o, this.conn);
12119                 if(this.activeRequest){
12120                     Roo.Ajax.abort(this.activeRequest);
12121                 }
12122                 this.activeRequest = Roo.Ajax.request(o);
12123             }else{
12124                 this.conn.request(o);
12125             }
12126         }else{
12127             callback.call(scope||this, null, arg, false);
12128         }
12129     },
12130
12131     // private
12132     loadResponse : function(o, success, response){
12133         delete this.activeRequest;
12134         if(!success){
12135             this.fireEvent("loadexception", this, o, response);
12136             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12137             return;
12138         }
12139         var result;
12140         try {
12141             result = o.reader.read(response);
12142         }catch(e){
12143             this.fireEvent("loadexception", this, o, response, e);
12144             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12145             return;
12146         }
12147         
12148         this.fireEvent("load", this, o, o.request.arg);
12149         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12150     },
12151
12152     // private
12153     update : function(dataSet){
12154
12155     },
12156
12157     // private
12158     updateResponse : function(dataSet){
12159
12160     }
12161 });/*
12162  * Based on:
12163  * Ext JS Library 1.1.1
12164  * Copyright(c) 2006-2007, Ext JS, LLC.
12165  *
12166  * Originally Released Under LGPL - original licence link has changed is not relivant.
12167  *
12168  * Fork - LGPL
12169  * <script type="text/javascript">
12170  */
12171
12172 /**
12173  * @class Roo.data.ScriptTagProxy
12174  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12175  * other than the originating domain of the running page.<br><br>
12176  * <p>
12177  * <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
12178  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12179  * <p>
12180  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12181  * source code that is used as the source inside a &lt;script> tag.<br><br>
12182  * <p>
12183  * In order for the browser to process the returned data, the server must wrap the data object
12184  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12185  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12186  * depending on whether the callback name was passed:
12187  * <p>
12188  * <pre><code>
12189 boolean scriptTag = false;
12190 String cb = request.getParameter("callback");
12191 if (cb != null) {
12192     scriptTag = true;
12193     response.setContentType("text/javascript");
12194 } else {
12195     response.setContentType("application/x-json");
12196 }
12197 Writer out = response.getWriter();
12198 if (scriptTag) {
12199     out.write(cb + "(");
12200 }
12201 out.print(dataBlock.toJsonString());
12202 if (scriptTag) {
12203     out.write(");");
12204 }
12205 </pre></code>
12206  *
12207  * @constructor
12208  * @param {Object} config A configuration object.
12209  */
12210 Roo.data.ScriptTagProxy = function(config){
12211     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12212     Roo.apply(this, config);
12213     this.head = document.getElementsByTagName("head")[0];
12214 };
12215
12216 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12217
12218 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12219     /**
12220      * @cfg {String} url The URL from which to request the data object.
12221      */
12222     /**
12223      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12224      */
12225     timeout : 30000,
12226     /**
12227      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12228      * the server the name of the callback function set up by the load call to process the returned data object.
12229      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12230      * javascript output which calls this named function passing the data object as its only parameter.
12231      */
12232     callbackParam : "callback",
12233     /**
12234      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12235      * name to the request.
12236      */
12237     nocache : true,
12238
12239     /**
12240      * Load data from the configured URL, read the data object into
12241      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12242      * process that block using the passed callback.
12243      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12244      * for the request to the remote server.
12245      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12246      * object into a block of Roo.data.Records.
12247      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12248      * The function must be passed <ul>
12249      * <li>The Record block object</li>
12250      * <li>The "arg" argument from the load function</li>
12251      * <li>A boolean success indicator</li>
12252      * </ul>
12253      * @param {Object} scope The scope in which to call the callback
12254      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12255      */
12256     load : function(params, reader, callback, scope, arg){
12257         if(this.fireEvent("beforeload", this, params) !== false){
12258
12259             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12260
12261             var url = this.url;
12262             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12263             if(this.nocache){
12264                 url += "&_dc=" + (new Date().getTime());
12265             }
12266             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12267             var trans = {
12268                 id : transId,
12269                 cb : "stcCallback"+transId,
12270                 scriptId : "stcScript"+transId,
12271                 params : params,
12272                 arg : arg,
12273                 url : url,
12274                 callback : callback,
12275                 scope : scope,
12276                 reader : reader
12277             };
12278             var conn = this;
12279
12280             window[trans.cb] = function(o){
12281                 conn.handleResponse(o, trans);
12282             };
12283
12284             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12285
12286             if(this.autoAbort !== false){
12287                 this.abort();
12288             }
12289
12290             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12291
12292             var script = document.createElement("script");
12293             script.setAttribute("src", url);
12294             script.setAttribute("type", "text/javascript");
12295             script.setAttribute("id", trans.scriptId);
12296             this.head.appendChild(script);
12297
12298             this.trans = trans;
12299         }else{
12300             callback.call(scope||this, null, arg, false);
12301         }
12302     },
12303
12304     // private
12305     isLoading : function(){
12306         return this.trans ? true : false;
12307     },
12308
12309     /**
12310      * Abort the current server request.
12311      */
12312     abort : function(){
12313         if(this.isLoading()){
12314             this.destroyTrans(this.trans);
12315         }
12316     },
12317
12318     // private
12319     destroyTrans : function(trans, isLoaded){
12320         this.head.removeChild(document.getElementById(trans.scriptId));
12321         clearTimeout(trans.timeoutId);
12322         if(isLoaded){
12323             window[trans.cb] = undefined;
12324             try{
12325                 delete window[trans.cb];
12326             }catch(e){}
12327         }else{
12328             // if hasn't been loaded, wait for load to remove it to prevent script error
12329             window[trans.cb] = function(){
12330                 window[trans.cb] = undefined;
12331                 try{
12332                     delete window[trans.cb];
12333                 }catch(e){}
12334             };
12335         }
12336     },
12337
12338     // private
12339     handleResponse : function(o, trans){
12340         this.trans = false;
12341         this.destroyTrans(trans, true);
12342         var result;
12343         try {
12344             result = trans.reader.readRecords(o);
12345         }catch(e){
12346             this.fireEvent("loadexception", this, o, trans.arg, e);
12347             trans.callback.call(trans.scope||window, null, trans.arg, false);
12348             return;
12349         }
12350         this.fireEvent("load", this, o, trans.arg);
12351         trans.callback.call(trans.scope||window, result, trans.arg, true);
12352     },
12353
12354     // private
12355     handleFailure : function(trans){
12356         this.trans = false;
12357         this.destroyTrans(trans, false);
12358         this.fireEvent("loadexception", this, null, trans.arg);
12359         trans.callback.call(trans.scope||window, null, trans.arg, false);
12360     }
12361 });/*
12362  * Based on:
12363  * Ext JS Library 1.1.1
12364  * Copyright(c) 2006-2007, Ext JS, LLC.
12365  *
12366  * Originally Released Under LGPL - original licence link has changed is not relivant.
12367  *
12368  * Fork - LGPL
12369  * <script type="text/javascript">
12370  */
12371
12372 /**
12373  * @class Roo.data.JsonReader
12374  * @extends Roo.data.DataReader
12375  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12376  * based on mappings in a provided Roo.data.Record constructor.
12377  * 
12378  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12379  * in the reply previously. 
12380  * 
12381  * <p>
12382  * Example code:
12383  * <pre><code>
12384 var RecordDef = Roo.data.Record.create([
12385     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12386     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12387 ]);
12388 var myReader = new Roo.data.JsonReader({
12389     totalProperty: "results",    // The property which contains the total dataset size (optional)
12390     root: "rows",                // The property which contains an Array of row objects
12391     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12392 }, RecordDef);
12393 </code></pre>
12394  * <p>
12395  * This would consume a JSON file like this:
12396  * <pre><code>
12397 { 'results': 2, 'rows': [
12398     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12399     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12400 }
12401 </code></pre>
12402  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12403  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12404  * paged from the remote server.
12405  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12406  * @cfg {String} root name of the property which contains the Array of row objects.
12407  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12408  * @cfg {Array} fields Array of field definition objects
12409  * @constructor
12410  * Create a new JsonReader
12411  * @param {Object} meta Metadata configuration options
12412  * @param {Object} recordType Either an Array of field definition objects,
12413  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12414  */
12415 Roo.data.JsonReader = function(meta, recordType){
12416     
12417     meta = meta || {};
12418     // set some defaults:
12419     Roo.applyIf(meta, {
12420         totalProperty: 'total',
12421         successProperty : 'success',
12422         root : 'data',
12423         id : 'id'
12424     });
12425     
12426     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12427 };
12428 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12429     
12430     /**
12431      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12432      * Used by Store query builder to append _requestMeta to params.
12433      * 
12434      */
12435     metaFromRemote : false,
12436     /**
12437      * This method is only used by a DataProxy which has retrieved data from a remote server.
12438      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12439      * @return {Object} data A data block which is used by an Roo.data.Store object as
12440      * a cache of Roo.data.Records.
12441      */
12442     read : function(response){
12443         var json = response.responseText;
12444        
12445         var o = /* eval:var:o */ eval("("+json+")");
12446         if(!o) {
12447             throw {message: "JsonReader.read: Json object not found"};
12448         }
12449         
12450         if(o.metaData){
12451             
12452             delete this.ef;
12453             this.metaFromRemote = true;
12454             this.meta = o.metaData;
12455             this.recordType = Roo.data.Record.create(o.metaData.fields);
12456             this.onMetaChange(this.meta, this.recordType, o);
12457         }
12458         return this.readRecords(o);
12459     },
12460
12461     // private function a store will implement
12462     onMetaChange : function(meta, recordType, o){
12463
12464     },
12465
12466     /**
12467          * @ignore
12468          */
12469     simpleAccess: function(obj, subsc) {
12470         return obj[subsc];
12471     },
12472
12473         /**
12474          * @ignore
12475          */
12476     getJsonAccessor: function(){
12477         var re = /[\[\.]/;
12478         return function(expr) {
12479             try {
12480                 return(re.test(expr))
12481                     ? new Function("obj", "return obj." + expr)
12482                     : function(obj){
12483                         return obj[expr];
12484                     };
12485             } catch(e){}
12486             return Roo.emptyFn;
12487         };
12488     }(),
12489
12490     /**
12491      * Create a data block containing Roo.data.Records from an XML document.
12492      * @param {Object} o An object which contains an Array of row objects in the property specified
12493      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12494      * which contains the total size of the dataset.
12495      * @return {Object} data A data block which is used by an Roo.data.Store object as
12496      * a cache of Roo.data.Records.
12497      */
12498     readRecords : function(o){
12499         /**
12500          * After any data loads, the raw JSON data is available for further custom processing.
12501          * @type Object
12502          */
12503         this.o = o;
12504         var s = this.meta, Record = this.recordType,
12505             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12506
12507 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12508         if (!this.ef) {
12509             if(s.totalProperty) {
12510                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12511                 }
12512                 if(s.successProperty) {
12513                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12514                 }
12515                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12516                 if (s.id) {
12517                         var g = this.getJsonAccessor(s.id);
12518                         this.getId = function(rec) {
12519                                 var r = g(rec);  
12520                                 return (r === undefined || r === "") ? null : r;
12521                         };
12522                 } else {
12523                         this.getId = function(){return null;};
12524                 }
12525             this.ef = [];
12526             for(var jj = 0; jj < fl; jj++){
12527                 f = fi[jj];
12528                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12529                 this.ef[jj] = this.getJsonAccessor(map);
12530             }
12531         }
12532
12533         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12534         if(s.totalProperty){
12535             var vt = parseInt(this.getTotal(o), 10);
12536             if(!isNaN(vt)){
12537                 totalRecords = vt;
12538             }
12539         }
12540         if(s.successProperty){
12541             var vs = this.getSuccess(o);
12542             if(vs === false || vs === 'false'){
12543                 success = false;
12544             }
12545         }
12546         var records = [];
12547         for(var i = 0; i < c; i++){
12548                 var n = root[i];
12549             var values = {};
12550             var id = this.getId(n);
12551             for(var j = 0; j < fl; j++){
12552                 f = fi[j];
12553             var v = this.ef[j](n);
12554             if (!f.convert) {
12555                 Roo.log('missing convert for ' + f.name);
12556                 Roo.log(f);
12557                 continue;
12558             }
12559             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12560             }
12561             var record = new Record(values, id);
12562             record.json = n;
12563             records[i] = record;
12564         }
12565         return {
12566             raw : o,
12567             success : success,
12568             records : records,
12569             totalRecords : totalRecords
12570         };
12571     }
12572 });/*
12573  * Based on:
12574  * Ext JS Library 1.1.1
12575  * Copyright(c) 2006-2007, Ext JS, LLC.
12576  *
12577  * Originally Released Under LGPL - original licence link has changed is not relivant.
12578  *
12579  * Fork - LGPL
12580  * <script type="text/javascript">
12581  */
12582
12583 /**
12584  * @class Roo.data.ArrayReader
12585  * @extends Roo.data.DataReader
12586  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12587  * Each element of that Array represents a row of data fields. The
12588  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12589  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12590  * <p>
12591  * Example code:.
12592  * <pre><code>
12593 var RecordDef = Roo.data.Record.create([
12594     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12595     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12596 ]);
12597 var myReader = new Roo.data.ArrayReader({
12598     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12599 }, RecordDef);
12600 </code></pre>
12601  * <p>
12602  * This would consume an Array like this:
12603  * <pre><code>
12604 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12605   </code></pre>
12606  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12607  * @constructor
12608  * Create a new JsonReader
12609  * @param {Object} meta Metadata configuration options.
12610  * @param {Object} recordType Either an Array of field definition objects
12611  * as specified to {@link Roo.data.Record#create},
12612  * or an {@link Roo.data.Record} object
12613  * created using {@link Roo.data.Record#create}.
12614  */
12615 Roo.data.ArrayReader = function(meta, recordType){
12616     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12617 };
12618
12619 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12620     /**
12621      * Create a data block containing Roo.data.Records from an XML document.
12622      * @param {Object} o An Array of row objects which represents the dataset.
12623      * @return {Object} data A data block which is used by an Roo.data.Store object as
12624      * a cache of Roo.data.Records.
12625      */
12626     readRecords : function(o){
12627         var sid = this.meta ? this.meta.id : null;
12628         var recordType = this.recordType, fields = recordType.prototype.fields;
12629         var records = [];
12630         var root = o;
12631             for(var i = 0; i < root.length; i++){
12632                     var n = root[i];
12633                 var values = {};
12634                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12635                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12636                 var f = fields.items[j];
12637                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12638                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12639                 v = f.convert(v);
12640                 values[f.name] = v;
12641             }
12642                 var record = new recordType(values, id);
12643                 record.json = n;
12644                 records[records.length] = record;
12645             }
12646             return {
12647                 records : records,
12648                 totalRecords : records.length
12649             };
12650     }
12651 });/*
12652  * - LGPL
12653  * * 
12654  */
12655
12656 /**
12657  * @class Roo.bootstrap.ComboBox
12658  * @extends Roo.bootstrap.TriggerField
12659  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12660  * @cfg {Boolean} append (true|false) default false
12661  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12662  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12663  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12664  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12665  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12666  * @cfg {Boolean} animate default true
12667  * @cfg {Boolean} emptyResultText only for touch device
12668  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12669  * @cfg {String} emptyTitle default ''
12670  * @constructor
12671  * Create a new ComboBox.
12672  * @param {Object} config Configuration options
12673  */
12674 Roo.bootstrap.ComboBox = function(config){
12675     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12676     this.addEvents({
12677         /**
12678          * @event expand
12679          * Fires when the dropdown list is expanded
12680         * @param {Roo.bootstrap.ComboBox} combo This combo box
12681         */
12682         'expand' : true,
12683         /**
12684          * @event collapse
12685          * Fires when the dropdown list is collapsed
12686         * @param {Roo.bootstrap.ComboBox} combo This combo box
12687         */
12688         'collapse' : true,
12689         /**
12690          * @event beforeselect
12691          * Fires before a list item is selected. Return false to cancel the selection.
12692         * @param {Roo.bootstrap.ComboBox} combo This combo box
12693         * @param {Roo.data.Record} record The data record returned from the underlying store
12694         * @param {Number} index The index of the selected item in the dropdown list
12695         */
12696         'beforeselect' : true,
12697         /**
12698          * @event select
12699          * Fires when a list item is selected
12700         * @param {Roo.bootstrap.ComboBox} combo This combo box
12701         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12702         * @param {Number} index The index of the selected item in the dropdown list
12703         */
12704         'select' : true,
12705         /**
12706          * @event beforequery
12707          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12708          * The event object passed has these properties:
12709         * @param {Roo.bootstrap.ComboBox} combo This combo box
12710         * @param {String} query The query
12711         * @param {Boolean} forceAll true to force "all" query
12712         * @param {Boolean} cancel true to cancel the query
12713         * @param {Object} e The query event object
12714         */
12715         'beforequery': true,
12716          /**
12717          * @event add
12718          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12719         * @param {Roo.bootstrap.ComboBox} combo This combo box
12720         */
12721         'add' : true,
12722         /**
12723          * @event edit
12724          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12725         * @param {Roo.bootstrap.ComboBox} combo This combo box
12726         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12727         */
12728         'edit' : true,
12729         /**
12730          * @event remove
12731          * Fires when the remove value from the combobox array
12732         * @param {Roo.bootstrap.ComboBox} combo This combo box
12733         */
12734         'remove' : true,
12735         /**
12736          * @event afterremove
12737          * Fires when the remove value from the combobox array
12738         * @param {Roo.bootstrap.ComboBox} combo This combo box
12739         */
12740         'afterremove' : true,
12741         /**
12742          * @event specialfilter
12743          * Fires when specialfilter
12744             * @param {Roo.bootstrap.ComboBox} combo This combo box
12745             */
12746         'specialfilter' : true,
12747         /**
12748          * @event tick
12749          * Fires when tick the element
12750             * @param {Roo.bootstrap.ComboBox} combo This combo box
12751             */
12752         'tick' : true,
12753         /**
12754          * @event touchviewdisplay
12755          * Fires when touch view require special display (default is using displayField)
12756             * @param {Roo.bootstrap.ComboBox} combo This combo box
12757             * @param {Object} cfg set html .
12758             */
12759         'touchviewdisplay' : true
12760         
12761     });
12762     
12763     this.item = [];
12764     this.tickItems = [];
12765     
12766     this.selectedIndex = -1;
12767     if(this.mode == 'local'){
12768         if(config.queryDelay === undefined){
12769             this.queryDelay = 10;
12770         }
12771         if(config.minChars === undefined){
12772             this.minChars = 0;
12773         }
12774     }
12775 };
12776
12777 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12778      
12779     /**
12780      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12781      * rendering into an Roo.Editor, defaults to false)
12782      */
12783     /**
12784      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12785      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12786      */
12787     /**
12788      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12789      */
12790     /**
12791      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12792      * the dropdown list (defaults to undefined, with no header element)
12793      */
12794
12795      /**
12796      * @cfg {String/Roo.Template} tpl The template to use to render the output
12797      */
12798      
12799      /**
12800      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12801      */
12802     listWidth: undefined,
12803     /**
12804      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12805      * mode = 'remote' or 'text' if mode = 'local')
12806      */
12807     displayField: undefined,
12808     
12809     /**
12810      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12811      * mode = 'remote' or 'value' if mode = 'local'). 
12812      * Note: use of a valueField requires the user make a selection
12813      * in order for a value to be mapped.
12814      */
12815     valueField: undefined,
12816     /**
12817      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12818      */
12819     modalTitle : '',
12820     
12821     /**
12822      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12823      * field's data value (defaults to the underlying DOM element's name)
12824      */
12825     hiddenName: undefined,
12826     /**
12827      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12828      */
12829     listClass: '',
12830     /**
12831      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12832      */
12833     selectedClass: 'active',
12834     
12835     /**
12836      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12837      */
12838     shadow:'sides',
12839     /**
12840      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12841      * anchor positions (defaults to 'tl-bl')
12842      */
12843     listAlign: 'tl-bl?',
12844     /**
12845      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12846      */
12847     maxHeight: 300,
12848     /**
12849      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12850      * query specified by the allQuery config option (defaults to 'query')
12851      */
12852     triggerAction: 'query',
12853     /**
12854      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12855      * (defaults to 4, does not apply if editable = false)
12856      */
12857     minChars : 4,
12858     /**
12859      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12860      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12861      */
12862     typeAhead: false,
12863     /**
12864      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12865      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12866      */
12867     queryDelay: 500,
12868     /**
12869      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12870      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12871      */
12872     pageSize: 0,
12873     /**
12874      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12875      * when editable = true (defaults to false)
12876      */
12877     selectOnFocus:false,
12878     /**
12879      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12880      */
12881     queryParam: 'query',
12882     /**
12883      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12884      * when mode = 'remote' (defaults to 'Loading...')
12885      */
12886     loadingText: 'Loading...',
12887     /**
12888      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12889      */
12890     resizable: false,
12891     /**
12892      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12893      */
12894     handleHeight : 8,
12895     /**
12896      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12897      * traditional select (defaults to true)
12898      */
12899     editable: true,
12900     /**
12901      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12902      */
12903     allQuery: '',
12904     /**
12905      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12906      */
12907     mode: 'remote',
12908     /**
12909      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12910      * listWidth has a higher value)
12911      */
12912     minListWidth : 70,
12913     /**
12914      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12915      * allow the user to set arbitrary text into the field (defaults to false)
12916      */
12917     forceSelection:false,
12918     /**
12919      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12920      * if typeAhead = true (defaults to 250)
12921      */
12922     typeAheadDelay : 250,
12923     /**
12924      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12925      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12926      */
12927     valueNotFoundText : undefined,
12928     /**
12929      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12930      */
12931     blockFocus : false,
12932     
12933     /**
12934      * @cfg {Boolean} disableClear Disable showing of clear button.
12935      */
12936     disableClear : false,
12937     /**
12938      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12939      */
12940     alwaysQuery : false,
12941     
12942     /**
12943      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12944      */
12945     multiple : false,
12946     
12947     /**
12948      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12949      */
12950     invalidClass : "has-warning",
12951     
12952     /**
12953      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12954      */
12955     validClass : "has-success",
12956     
12957     /**
12958      * @cfg {Boolean} specialFilter (true|false) special filter default false
12959      */
12960     specialFilter : false,
12961     
12962     /**
12963      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12964      */
12965     mobileTouchView : true,
12966     
12967     /**
12968      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12969      */
12970     useNativeIOS : false,
12971     
12972     ios_options : false,
12973     
12974     //private
12975     addicon : false,
12976     editicon: false,
12977     
12978     page: 0,
12979     hasQuery: false,
12980     append: false,
12981     loadNext: false,
12982     autoFocus : true,
12983     tickable : false,
12984     btnPosition : 'right',
12985     triggerList : true,
12986     showToggleBtn : true,
12987     animate : true,
12988     emptyResultText: 'Empty',
12989     triggerText : 'Select',
12990     emptyTitle : '',
12991     
12992     // element that contains real text value.. (when hidden is used..)
12993     
12994     getAutoCreate : function()
12995     {   
12996         var cfg = false;
12997         //render
12998         /*
12999          * Render classic select for iso
13000          */
13001         
13002         if(Roo.isIOS && this.useNativeIOS){
13003             cfg = this.getAutoCreateNativeIOS();
13004             return cfg;
13005         }
13006         
13007         /*
13008          * Touch Devices
13009          */
13010         
13011         if(Roo.isTouch && this.mobileTouchView){
13012             cfg = this.getAutoCreateTouchView();
13013             return cfg;;
13014         }
13015         
13016         /*
13017          *  Normal ComboBox
13018          */
13019         if(!this.tickable){
13020             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13021             return cfg;
13022         }
13023         
13024         /*
13025          *  ComboBox with tickable selections
13026          */
13027              
13028         var align = this.labelAlign || this.parentLabelAlign();
13029         
13030         cfg = {
13031             cls : 'form-group roo-combobox-tickable' //input-group
13032         };
13033         
13034         var btn_text_select = '';
13035         var btn_text_done = '';
13036         var btn_text_cancel = '';
13037         
13038         if (this.btn_text_show) {
13039             btn_text_select = 'Select';
13040             btn_text_done = 'Done';
13041             btn_text_cancel = 'Cancel'; 
13042         }
13043         
13044         var buttons = {
13045             tag : 'div',
13046             cls : 'tickable-buttons',
13047             cn : [
13048                 {
13049                     tag : 'button',
13050                     type : 'button',
13051                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13052                     //html : this.triggerText
13053                     html: btn_text_select
13054                 },
13055                 {
13056                     tag : 'button',
13057                     type : 'button',
13058                     name : 'ok',
13059                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13060                     //html : 'Done'
13061                     html: btn_text_done
13062                 },
13063                 {
13064                     tag : 'button',
13065                     type : 'button',
13066                     name : 'cancel',
13067                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13068                     //html : 'Cancel'
13069                     html: btn_text_cancel
13070                 }
13071             ]
13072         };
13073         
13074         if(this.editable){
13075             buttons.cn.unshift({
13076                 tag: 'input',
13077                 cls: 'roo-select2-search-field-input'
13078             });
13079         }
13080         
13081         var _this = this;
13082         
13083         Roo.each(buttons.cn, function(c){
13084             if (_this.size) {
13085                 c.cls += ' btn-' + _this.size;
13086             }
13087
13088             if (_this.disabled) {
13089                 c.disabled = true;
13090             }
13091         });
13092         
13093         var box = {
13094             tag: 'div',
13095             cn: [
13096                 {
13097                     tag: 'input',
13098                     type : 'hidden',
13099                     cls: 'form-hidden-field'
13100                 },
13101                 {
13102                     tag: 'ul',
13103                     cls: 'roo-select2-choices',
13104                     cn:[
13105                         {
13106                             tag: 'li',
13107                             cls: 'roo-select2-search-field',
13108                             cn: [
13109                                 buttons
13110                             ]
13111                         }
13112                     ]
13113                 }
13114             ]
13115         };
13116         
13117         var combobox = {
13118             cls: 'roo-select2-container input-group roo-select2-container-multi',
13119             cn: [
13120                 box
13121 //                {
13122 //                    tag: 'ul',
13123 //                    cls: 'typeahead typeahead-long dropdown-menu',
13124 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13125 //                }
13126             ]
13127         };
13128         
13129         if(this.hasFeedback && !this.allowBlank){
13130             
13131             var feedback = {
13132                 tag: 'span',
13133                 cls: 'glyphicon form-control-feedback'
13134             };
13135
13136             combobox.cn.push(feedback);
13137         }
13138         
13139         
13140         if (align ==='left' && this.fieldLabel.length) {
13141             
13142             cfg.cls += ' roo-form-group-label-left';
13143             
13144             cfg.cn = [
13145                 {
13146                     tag : 'i',
13147                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13148                     tooltip : 'This field is required'
13149                 },
13150                 {
13151                     tag: 'label',
13152                     'for' :  id,
13153                     cls : 'control-label',
13154                     html : this.fieldLabel
13155
13156                 },
13157                 {
13158                     cls : "", 
13159                     cn: [
13160                         combobox
13161                     ]
13162                 }
13163
13164             ];
13165             
13166             var labelCfg = cfg.cn[1];
13167             var contentCfg = cfg.cn[2];
13168             
13169
13170             if(this.indicatorpos == 'right'){
13171                 
13172                 cfg.cn = [
13173                     {
13174                         tag: 'label',
13175                         'for' :  id,
13176                         cls : 'control-label',
13177                         cn : [
13178                             {
13179                                 tag : 'span',
13180                                 html : this.fieldLabel
13181                             },
13182                             {
13183                                 tag : 'i',
13184                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13185                                 tooltip : 'This field is required'
13186                             }
13187                         ]
13188                     },
13189                     {
13190                         cls : "",
13191                         cn: [
13192                             combobox
13193                         ]
13194                     }
13195
13196                 ];
13197                 
13198                 
13199                 
13200                 labelCfg = cfg.cn[0];
13201                 contentCfg = cfg.cn[1];
13202             
13203             }
13204             
13205             if(this.labelWidth > 12){
13206                 labelCfg.style = "width: " + this.labelWidth + 'px';
13207             }
13208             
13209             if(this.labelWidth < 13 && this.labelmd == 0){
13210                 this.labelmd = this.labelWidth;
13211             }
13212             
13213             if(this.labellg > 0){
13214                 labelCfg.cls += ' col-lg-' + this.labellg;
13215                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13216             }
13217             
13218             if(this.labelmd > 0){
13219                 labelCfg.cls += ' col-md-' + this.labelmd;
13220                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13221             }
13222             
13223             if(this.labelsm > 0){
13224                 labelCfg.cls += ' col-sm-' + this.labelsm;
13225                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13226             }
13227             
13228             if(this.labelxs > 0){
13229                 labelCfg.cls += ' col-xs-' + this.labelxs;
13230                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13231             }
13232                 
13233                 
13234         } else if ( this.fieldLabel.length) {
13235 //                Roo.log(" label");
13236                  cfg.cn = [
13237                     {
13238                         tag : 'i',
13239                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13240                         tooltip : 'This field is required'
13241                     },
13242                     {
13243                         tag: 'label',
13244                         //cls : 'input-group-addon',
13245                         html : this.fieldLabel
13246                     },
13247                     combobox
13248                 ];
13249                 
13250                 if(this.indicatorpos == 'right'){
13251                     cfg.cn = [
13252                         {
13253                             tag: 'label',
13254                             //cls : 'input-group-addon',
13255                             html : this.fieldLabel
13256                         },
13257                         {
13258                             tag : 'i',
13259                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13260                             tooltip : 'This field is required'
13261                         },
13262                         combobox
13263                     ];
13264                     
13265                 }
13266
13267         } else {
13268             
13269 //                Roo.log(" no label && no align");
13270                 cfg = combobox
13271                      
13272                 
13273         }
13274          
13275         var settings=this;
13276         ['xs','sm','md','lg'].map(function(size){
13277             if (settings[size]) {
13278                 cfg.cls += ' col-' + size + '-' + settings[size];
13279             }
13280         });
13281         
13282         return cfg;
13283         
13284     },
13285     
13286     _initEventsCalled : false,
13287     
13288     // private
13289     initEvents: function()
13290     {   
13291         if (this._initEventsCalled) { // as we call render... prevent looping...
13292             return;
13293         }
13294         this._initEventsCalled = true;
13295         
13296         if (!this.store) {
13297             throw "can not find store for combo";
13298         }
13299         
13300         this.indicator = this.indicatorEl();
13301         
13302         this.store = Roo.factory(this.store, Roo.data);
13303         this.store.parent = this;
13304         
13305         // if we are building from html. then this element is so complex, that we can not really
13306         // use the rendered HTML.
13307         // so we have to trash and replace the previous code.
13308         if (Roo.XComponent.build_from_html) {
13309             // remove this element....
13310             var e = this.el.dom, k=0;
13311             while (e ) { e = e.previousSibling;  ++k;}
13312
13313             this.el.remove();
13314             
13315             this.el=false;
13316             this.rendered = false;
13317             
13318             this.render(this.parent().getChildContainer(true), k);
13319         }
13320         
13321         if(Roo.isIOS && this.useNativeIOS){
13322             this.initIOSView();
13323             return;
13324         }
13325         
13326         /*
13327          * Touch Devices
13328          */
13329         
13330         if(Roo.isTouch && this.mobileTouchView){
13331             this.initTouchView();
13332             return;
13333         }
13334         
13335         if(this.tickable){
13336             this.initTickableEvents();
13337             return;
13338         }
13339         
13340         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13341         
13342         if(this.hiddenName){
13343             
13344             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13345             
13346             this.hiddenField.dom.value =
13347                 this.hiddenValue !== undefined ? this.hiddenValue :
13348                 this.value !== undefined ? this.value : '';
13349
13350             // prevent input submission
13351             this.el.dom.removeAttribute('name');
13352             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13353              
13354              
13355         }
13356         //if(Roo.isGecko){
13357         //    this.el.dom.setAttribute('autocomplete', 'off');
13358         //}
13359         
13360         var cls = 'x-combo-list';
13361         
13362         //this.list = new Roo.Layer({
13363         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13364         //});
13365         
13366         var _this = this;
13367         
13368         (function(){
13369             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13370             _this.list.setWidth(lw);
13371         }).defer(100);
13372         
13373         this.list.on('mouseover', this.onViewOver, this);
13374         this.list.on('mousemove', this.onViewMove, this);
13375         this.list.on('scroll', this.onViewScroll, this);
13376         
13377         /*
13378         this.list.swallowEvent('mousewheel');
13379         this.assetHeight = 0;
13380
13381         if(this.title){
13382             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13383             this.assetHeight += this.header.getHeight();
13384         }
13385
13386         this.innerList = this.list.createChild({cls:cls+'-inner'});
13387         this.innerList.on('mouseover', this.onViewOver, this);
13388         this.innerList.on('mousemove', this.onViewMove, this);
13389         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13390         
13391         if(this.allowBlank && !this.pageSize && !this.disableClear){
13392             this.footer = this.list.createChild({cls:cls+'-ft'});
13393             this.pageTb = new Roo.Toolbar(this.footer);
13394            
13395         }
13396         if(this.pageSize){
13397             this.footer = this.list.createChild({cls:cls+'-ft'});
13398             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13399                     {pageSize: this.pageSize});
13400             
13401         }
13402         
13403         if (this.pageTb && this.allowBlank && !this.disableClear) {
13404             var _this = this;
13405             this.pageTb.add(new Roo.Toolbar.Fill(), {
13406                 cls: 'x-btn-icon x-btn-clear',
13407                 text: '&#160;',
13408                 handler: function()
13409                 {
13410                     _this.collapse();
13411                     _this.clearValue();
13412                     _this.onSelect(false, -1);
13413                 }
13414             });
13415         }
13416         if (this.footer) {
13417             this.assetHeight += this.footer.getHeight();
13418         }
13419         */
13420             
13421         if(!this.tpl){
13422             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13423         }
13424
13425         this.view = new Roo.View(this.list, this.tpl, {
13426             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13427         });
13428         //this.view.wrapEl.setDisplayed(false);
13429         this.view.on('click', this.onViewClick, this);
13430         
13431         
13432         this.store.on('beforeload', this.onBeforeLoad, this);
13433         this.store.on('load', this.onLoad, this);
13434         this.store.on('loadexception', this.onLoadException, this);
13435         /*
13436         if(this.resizable){
13437             this.resizer = new Roo.Resizable(this.list,  {
13438                pinned:true, handles:'se'
13439             });
13440             this.resizer.on('resize', function(r, w, h){
13441                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13442                 this.listWidth = w;
13443                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13444                 this.restrictHeight();
13445             }, this);
13446             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13447         }
13448         */
13449         if(!this.editable){
13450             this.editable = true;
13451             this.setEditable(false);
13452         }
13453         
13454         /*
13455         
13456         if (typeof(this.events.add.listeners) != 'undefined') {
13457             
13458             this.addicon = this.wrap.createChild(
13459                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13460        
13461             this.addicon.on('click', function(e) {
13462                 this.fireEvent('add', this);
13463             }, this);
13464         }
13465         if (typeof(this.events.edit.listeners) != 'undefined') {
13466             
13467             this.editicon = this.wrap.createChild(
13468                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13469             if (this.addicon) {
13470                 this.editicon.setStyle('margin-left', '40px');
13471             }
13472             this.editicon.on('click', function(e) {
13473                 
13474                 // we fire even  if inothing is selected..
13475                 this.fireEvent('edit', this, this.lastData );
13476                 
13477             }, this);
13478         }
13479         */
13480         
13481         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13482             "up" : function(e){
13483                 this.inKeyMode = true;
13484                 this.selectPrev();
13485             },
13486
13487             "down" : function(e){
13488                 if(!this.isExpanded()){
13489                     this.onTriggerClick();
13490                 }else{
13491                     this.inKeyMode = true;
13492                     this.selectNext();
13493                 }
13494             },
13495
13496             "enter" : function(e){
13497 //                this.onViewClick();
13498                 //return true;
13499                 this.collapse();
13500                 
13501                 if(this.fireEvent("specialkey", this, e)){
13502                     this.onViewClick(false);
13503                 }
13504                 
13505                 return true;
13506             },
13507
13508             "esc" : function(e){
13509                 this.collapse();
13510             },
13511
13512             "tab" : function(e){
13513                 this.collapse();
13514                 
13515                 if(this.fireEvent("specialkey", this, e)){
13516                     this.onViewClick(false);
13517                 }
13518                 
13519                 return true;
13520             },
13521
13522             scope : this,
13523
13524             doRelay : function(foo, bar, hname){
13525                 if(hname == 'down' || this.scope.isExpanded()){
13526                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13527                 }
13528                 return true;
13529             },
13530
13531             forceKeyDown: true
13532         });
13533         
13534         
13535         this.queryDelay = Math.max(this.queryDelay || 10,
13536                 this.mode == 'local' ? 10 : 250);
13537         
13538         
13539         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13540         
13541         if(this.typeAhead){
13542             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13543         }
13544         if(this.editable !== false){
13545             this.inputEl().on("keyup", this.onKeyUp, this);
13546         }
13547         if(this.forceSelection){
13548             this.inputEl().on('blur', this.doForce, this);
13549         }
13550         
13551         if(this.multiple){
13552             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13553             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13554         }
13555     },
13556     
13557     initTickableEvents: function()
13558     {   
13559         this.createList();
13560         
13561         if(this.hiddenName){
13562             
13563             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13564             
13565             this.hiddenField.dom.value =
13566                 this.hiddenValue !== undefined ? this.hiddenValue :
13567                 this.value !== undefined ? this.value : '';
13568
13569             // prevent input submission
13570             this.el.dom.removeAttribute('name');
13571             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13572              
13573              
13574         }
13575         
13576 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13577         
13578         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13579         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13580         if(this.triggerList){
13581             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13582         }
13583          
13584         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13585         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13586         
13587         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13588         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13589         
13590         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13591         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13592         
13593         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13594         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13595         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13596         
13597         this.okBtn.hide();
13598         this.cancelBtn.hide();
13599         
13600         var _this = this;
13601         
13602         (function(){
13603             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13604             _this.list.setWidth(lw);
13605         }).defer(100);
13606         
13607         this.list.on('mouseover', this.onViewOver, this);
13608         this.list.on('mousemove', this.onViewMove, this);
13609         
13610         this.list.on('scroll', this.onViewScroll, this);
13611         
13612         if(!this.tpl){
13613             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13614                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13615         }
13616
13617         this.view = new Roo.View(this.list, this.tpl, {
13618             singleSelect:true,
13619             tickable:true,
13620             parent:this,
13621             store: this.store,
13622             selectedClass: this.selectedClass
13623         });
13624         
13625         //this.view.wrapEl.setDisplayed(false);
13626         this.view.on('click', this.onViewClick, this);
13627         
13628         
13629         
13630         this.store.on('beforeload', this.onBeforeLoad, this);
13631         this.store.on('load', this.onLoad, this);
13632         this.store.on('loadexception', this.onLoadException, this);
13633         
13634         if(this.editable){
13635             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13636                 "up" : function(e){
13637                     this.inKeyMode = true;
13638                     this.selectPrev();
13639                 },
13640
13641                 "down" : function(e){
13642                     this.inKeyMode = true;
13643                     this.selectNext();
13644                 },
13645
13646                 "enter" : function(e){
13647                     if(this.fireEvent("specialkey", this, e)){
13648                         this.onViewClick(false);
13649                     }
13650                     
13651                     return true;
13652                 },
13653
13654                 "esc" : function(e){
13655                     this.onTickableFooterButtonClick(e, false, false);
13656                 },
13657
13658                 "tab" : function(e){
13659                     this.fireEvent("specialkey", this, e);
13660                     
13661                     this.onTickableFooterButtonClick(e, false, false);
13662                     
13663                     return true;
13664                 },
13665
13666                 scope : this,
13667
13668                 doRelay : function(e, fn, key){
13669                     if(this.scope.isExpanded()){
13670                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13671                     }
13672                     return true;
13673                 },
13674
13675                 forceKeyDown: true
13676             });
13677         }
13678         
13679         this.queryDelay = Math.max(this.queryDelay || 10,
13680                 this.mode == 'local' ? 10 : 250);
13681         
13682         
13683         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13684         
13685         if(this.typeAhead){
13686             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13687         }
13688         
13689         if(this.editable !== false){
13690             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13691         }
13692         
13693         this.indicator = this.indicatorEl();
13694         
13695         if(this.indicator){
13696             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13697             this.indicator.hide();
13698         }
13699         
13700     },
13701
13702     onDestroy : function(){
13703         if(this.view){
13704             this.view.setStore(null);
13705             this.view.el.removeAllListeners();
13706             this.view.el.remove();
13707             this.view.purgeListeners();
13708         }
13709         if(this.list){
13710             this.list.dom.innerHTML  = '';
13711         }
13712         
13713         if(this.store){
13714             this.store.un('beforeload', this.onBeforeLoad, this);
13715             this.store.un('load', this.onLoad, this);
13716             this.store.un('loadexception', this.onLoadException, this);
13717         }
13718         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13719     },
13720
13721     // private
13722     fireKey : function(e){
13723         if(e.isNavKeyPress() && !this.list.isVisible()){
13724             this.fireEvent("specialkey", this, e);
13725         }
13726     },
13727
13728     // private
13729     onResize: function(w, h){
13730 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13731 //        
13732 //        if(typeof w != 'number'){
13733 //            // we do not handle it!?!?
13734 //            return;
13735 //        }
13736 //        var tw = this.trigger.getWidth();
13737 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13738 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13739 //        var x = w - tw;
13740 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13741 //            
13742 //        //this.trigger.setStyle('left', x+'px');
13743 //        
13744 //        if(this.list && this.listWidth === undefined){
13745 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13746 //            this.list.setWidth(lw);
13747 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13748 //        }
13749         
13750     
13751         
13752     },
13753
13754     /**
13755      * Allow or prevent the user from directly editing the field text.  If false is passed,
13756      * the user will only be able to select from the items defined in the dropdown list.  This method
13757      * is the runtime equivalent of setting the 'editable' config option at config time.
13758      * @param {Boolean} value True to allow the user to directly edit the field text
13759      */
13760     setEditable : function(value){
13761         if(value == this.editable){
13762             return;
13763         }
13764         this.editable = value;
13765         if(!value){
13766             this.inputEl().dom.setAttribute('readOnly', true);
13767             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13768             this.inputEl().addClass('x-combo-noedit');
13769         }else{
13770             this.inputEl().dom.setAttribute('readOnly', false);
13771             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13772             this.inputEl().removeClass('x-combo-noedit');
13773         }
13774     },
13775
13776     // private
13777     
13778     onBeforeLoad : function(combo,opts){
13779         if(!this.hasFocus){
13780             return;
13781         }
13782          if (!opts.add) {
13783             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13784          }
13785         this.restrictHeight();
13786         this.selectedIndex = -1;
13787     },
13788
13789     // private
13790     onLoad : function(){
13791         
13792         this.hasQuery = false;
13793         
13794         if(!this.hasFocus){
13795             return;
13796         }
13797         
13798         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13799             this.loading.hide();
13800         }
13801         
13802         if(this.store.getCount() > 0){
13803             
13804             this.expand();
13805             this.restrictHeight();
13806             if(this.lastQuery == this.allQuery){
13807                 if(this.editable && !this.tickable){
13808                     this.inputEl().dom.select();
13809                 }
13810                 
13811                 if(
13812                     !this.selectByValue(this.value, true) &&
13813                     this.autoFocus && 
13814                     (
13815                         !this.store.lastOptions ||
13816                         typeof(this.store.lastOptions.add) == 'undefined' || 
13817                         this.store.lastOptions.add != true
13818                     )
13819                 ){
13820                     this.select(0, true);
13821                 }
13822             }else{
13823                 if(this.autoFocus){
13824                     this.selectNext();
13825                 }
13826                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13827                     this.taTask.delay(this.typeAheadDelay);
13828                 }
13829             }
13830         }else{
13831             this.onEmptyResults();
13832         }
13833         
13834         //this.el.focus();
13835     },
13836     // private
13837     onLoadException : function()
13838     {
13839         this.hasQuery = false;
13840         
13841         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13842             this.loading.hide();
13843         }
13844         
13845         if(this.tickable && this.editable){
13846             return;
13847         }
13848         
13849         this.collapse();
13850         // only causes errors at present
13851         //Roo.log(this.store.reader.jsonData);
13852         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13853             // fixme
13854             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13855         //}
13856         
13857         
13858     },
13859     // private
13860     onTypeAhead : function(){
13861         if(this.store.getCount() > 0){
13862             var r = this.store.getAt(0);
13863             var newValue = r.data[this.displayField];
13864             var len = newValue.length;
13865             var selStart = this.getRawValue().length;
13866             
13867             if(selStart != len){
13868                 this.setRawValue(newValue);
13869                 this.selectText(selStart, newValue.length);
13870             }
13871         }
13872     },
13873
13874     // private
13875     onSelect : function(record, index){
13876         
13877         if(this.fireEvent('beforeselect', this, record, index) !== false){
13878         
13879             this.setFromData(index > -1 ? record.data : false);
13880             
13881             this.collapse();
13882             this.fireEvent('select', this, record, index);
13883         }
13884     },
13885
13886     /**
13887      * Returns the currently selected field value or empty string if no value is set.
13888      * @return {String} value The selected value
13889      */
13890     getValue : function()
13891     {
13892         if(Roo.isIOS && this.useNativeIOS){
13893             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13894         }
13895         
13896         if(this.multiple){
13897             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13898         }
13899         
13900         if(this.valueField){
13901             return typeof this.value != 'undefined' ? this.value : '';
13902         }else{
13903             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13904         }
13905     },
13906     
13907     getRawValue : function()
13908     {
13909         if(Roo.isIOS && this.useNativeIOS){
13910             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13911         }
13912         
13913         var v = this.inputEl().getValue();
13914         
13915         return v;
13916     },
13917
13918     /**
13919      * Clears any text/value currently set in the field
13920      */
13921     clearValue : function(){
13922         
13923         if(this.hiddenField){
13924             this.hiddenField.dom.value = '';
13925         }
13926         this.value = '';
13927         this.setRawValue('');
13928         this.lastSelectionText = '';
13929         this.lastData = false;
13930         
13931         var close = this.closeTriggerEl();
13932         
13933         if(close){
13934             close.hide();
13935         }
13936         
13937         this.validate();
13938         
13939     },
13940
13941     /**
13942      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13943      * will be displayed in the field.  If the value does not match the data value of an existing item,
13944      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13945      * Otherwise the field will be blank (although the value will still be set).
13946      * @param {String} value The value to match
13947      */
13948     setValue : function(v)
13949     {
13950         if(Roo.isIOS && this.useNativeIOS){
13951             this.setIOSValue(v);
13952             return;
13953         }
13954         
13955         if(this.multiple){
13956             this.syncValue();
13957             return;
13958         }
13959         
13960         var text = v;
13961         if(this.valueField){
13962             var r = this.findRecord(this.valueField, v);
13963             if(r){
13964                 text = r.data[this.displayField];
13965             }else if(this.valueNotFoundText !== undefined){
13966                 text = this.valueNotFoundText;
13967             }
13968         }
13969         this.lastSelectionText = text;
13970         if(this.hiddenField){
13971             this.hiddenField.dom.value = v;
13972         }
13973         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13974         this.value = v;
13975         
13976         var close = this.closeTriggerEl();
13977         
13978         if(close){
13979             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13980         }
13981         
13982         this.validate();
13983     },
13984     /**
13985      * @property {Object} the last set data for the element
13986      */
13987     
13988     lastData : false,
13989     /**
13990      * Sets the value of the field based on a object which is related to the record format for the store.
13991      * @param {Object} value the value to set as. or false on reset?
13992      */
13993     setFromData : function(o){
13994         
13995         if(this.multiple){
13996             this.addItem(o);
13997             return;
13998         }
13999             
14000         var dv = ''; // display value
14001         var vv = ''; // value value..
14002         this.lastData = o;
14003         if (this.displayField) {
14004             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14005         } else {
14006             // this is an error condition!!!
14007             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14008         }
14009         
14010         if(this.valueField){
14011             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14012         }
14013         
14014         var close = this.closeTriggerEl();
14015         
14016         if(close){
14017             if(dv.length || vv * 1 > 0){
14018                 close.show() ;
14019                 this.blockFocus=true;
14020             } else {
14021                 close.hide();
14022             }             
14023         }
14024         
14025         if(this.hiddenField){
14026             this.hiddenField.dom.value = vv;
14027             
14028             this.lastSelectionText = dv;
14029             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14030             this.value = vv;
14031             return;
14032         }
14033         // no hidden field.. - we store the value in 'value', but still display
14034         // display field!!!!
14035         this.lastSelectionText = dv;
14036         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14037         this.value = vv;
14038         
14039         
14040         
14041     },
14042     // private
14043     reset : function(){
14044         // overridden so that last data is reset..
14045         
14046         if(this.multiple){
14047             this.clearItem();
14048             return;
14049         }
14050         
14051         this.setValue(this.originalValue);
14052         //this.clearInvalid();
14053         this.lastData = false;
14054         if (this.view) {
14055             this.view.clearSelections();
14056         }
14057         
14058         this.validate();
14059     },
14060     // private
14061     findRecord : function(prop, value){
14062         var record;
14063         if(this.store.getCount() > 0){
14064             this.store.each(function(r){
14065                 if(r.data[prop] == value){
14066                     record = r;
14067                     return false;
14068                 }
14069                 return true;
14070             });
14071         }
14072         return record;
14073     },
14074     
14075     getName: function()
14076     {
14077         // returns hidden if it's set..
14078         if (!this.rendered) {return ''};
14079         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14080         
14081     },
14082     // private
14083     onViewMove : function(e, t){
14084         this.inKeyMode = false;
14085     },
14086
14087     // private
14088     onViewOver : function(e, t){
14089         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14090             return;
14091         }
14092         var item = this.view.findItemFromChild(t);
14093         
14094         if(item){
14095             var index = this.view.indexOf(item);
14096             this.select(index, false);
14097         }
14098     },
14099
14100     // private
14101     onViewClick : function(view, doFocus, el, e)
14102     {
14103         var index = this.view.getSelectedIndexes()[0];
14104         
14105         var r = this.store.getAt(index);
14106         
14107         if(this.tickable){
14108             
14109             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14110                 return;
14111             }
14112             
14113             var rm = false;
14114             var _this = this;
14115             
14116             Roo.each(this.tickItems, function(v,k){
14117                 
14118                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14119                     Roo.log(v);
14120                     _this.tickItems.splice(k, 1);
14121                     
14122                     if(typeof(e) == 'undefined' && view == false){
14123                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14124                     }
14125                     
14126                     rm = true;
14127                     return;
14128                 }
14129             });
14130             
14131             if(rm){
14132                 return;
14133             }
14134             
14135             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14136                 this.tickItems.push(r.data);
14137             }
14138             
14139             if(typeof(e) == 'undefined' && view == false){
14140                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14141             }
14142                     
14143             return;
14144         }
14145         
14146         if(r){
14147             this.onSelect(r, index);
14148         }
14149         if(doFocus !== false && !this.blockFocus){
14150             this.inputEl().focus();
14151         }
14152     },
14153
14154     // private
14155     restrictHeight : function(){
14156         //this.innerList.dom.style.height = '';
14157         //var inner = this.innerList.dom;
14158         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14159         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14160         //this.list.beginUpdate();
14161         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14162         this.list.alignTo(this.inputEl(), this.listAlign);
14163         this.list.alignTo(this.inputEl(), this.listAlign);
14164         //this.list.endUpdate();
14165     },
14166
14167     // private
14168     onEmptyResults : function(){
14169         
14170         if(this.tickable && this.editable){
14171             this.hasFocus = false;
14172             this.restrictHeight();
14173             return;
14174         }
14175         
14176         this.collapse();
14177     },
14178
14179     /**
14180      * Returns true if the dropdown list is expanded, else false.
14181      */
14182     isExpanded : function(){
14183         return this.list.isVisible();
14184     },
14185
14186     /**
14187      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14188      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14189      * @param {String} value The data value of the item to select
14190      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14191      * selected item if it is not currently in view (defaults to true)
14192      * @return {Boolean} True if the value matched an item in the list, else false
14193      */
14194     selectByValue : function(v, scrollIntoView){
14195         if(v !== undefined && v !== null){
14196             var r = this.findRecord(this.valueField || this.displayField, v);
14197             if(r){
14198                 this.select(this.store.indexOf(r), scrollIntoView);
14199                 return true;
14200             }
14201         }
14202         return false;
14203     },
14204
14205     /**
14206      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14207      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14208      * @param {Number} index The zero-based index of the list item to select
14209      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14210      * selected item if it is not currently in view (defaults to true)
14211      */
14212     select : function(index, scrollIntoView){
14213         this.selectedIndex = index;
14214         this.view.select(index);
14215         if(scrollIntoView !== false){
14216             var el = this.view.getNode(index);
14217             /*
14218              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14219              */
14220             if(el){
14221                 this.list.scrollChildIntoView(el, false);
14222             }
14223         }
14224     },
14225
14226     // private
14227     selectNext : function(){
14228         var ct = this.store.getCount();
14229         if(ct > 0){
14230             if(this.selectedIndex == -1){
14231                 this.select(0);
14232             }else if(this.selectedIndex < ct-1){
14233                 this.select(this.selectedIndex+1);
14234             }
14235         }
14236     },
14237
14238     // private
14239     selectPrev : function(){
14240         var ct = this.store.getCount();
14241         if(ct > 0){
14242             if(this.selectedIndex == -1){
14243                 this.select(0);
14244             }else if(this.selectedIndex != 0){
14245                 this.select(this.selectedIndex-1);
14246             }
14247         }
14248     },
14249
14250     // private
14251     onKeyUp : function(e){
14252         if(this.editable !== false && !e.isSpecialKey()){
14253             this.lastKey = e.getKey();
14254             this.dqTask.delay(this.queryDelay);
14255         }
14256     },
14257
14258     // private
14259     validateBlur : function(){
14260         return !this.list || !this.list.isVisible();   
14261     },
14262
14263     // private
14264     initQuery : function(){
14265         
14266         var v = this.getRawValue();
14267         
14268         if(this.tickable && this.editable){
14269             v = this.tickableInputEl().getValue();
14270         }
14271         
14272         this.doQuery(v);
14273     },
14274
14275     // private
14276     doForce : function(){
14277         if(this.inputEl().dom.value.length > 0){
14278             this.inputEl().dom.value =
14279                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14280              
14281         }
14282     },
14283
14284     /**
14285      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14286      * query allowing the query action to be canceled if needed.
14287      * @param {String} query The SQL query to execute
14288      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14289      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14290      * saved in the current store (defaults to false)
14291      */
14292     doQuery : function(q, forceAll){
14293         
14294         if(q === undefined || q === null){
14295             q = '';
14296         }
14297         var qe = {
14298             query: q,
14299             forceAll: forceAll,
14300             combo: this,
14301             cancel:false
14302         };
14303         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14304             return false;
14305         }
14306         q = qe.query;
14307         
14308         forceAll = qe.forceAll;
14309         if(forceAll === true || (q.length >= this.minChars)){
14310             
14311             this.hasQuery = true;
14312             
14313             if(this.lastQuery != q || this.alwaysQuery){
14314                 this.lastQuery = q;
14315                 if(this.mode == 'local'){
14316                     this.selectedIndex = -1;
14317                     if(forceAll){
14318                         this.store.clearFilter();
14319                     }else{
14320                         
14321                         if(this.specialFilter){
14322                             this.fireEvent('specialfilter', this);
14323                             this.onLoad();
14324                             return;
14325                         }
14326                         
14327                         this.store.filter(this.displayField, q);
14328                     }
14329                     
14330                     this.store.fireEvent("datachanged", this.store);
14331                     
14332                     this.onLoad();
14333                     
14334                     
14335                 }else{
14336                     
14337                     this.store.baseParams[this.queryParam] = q;
14338                     
14339                     var options = {params : this.getParams(q)};
14340                     
14341                     if(this.loadNext){
14342                         options.add = true;
14343                         options.params.start = this.page * this.pageSize;
14344                     }
14345                     
14346                     this.store.load(options);
14347                     
14348                     /*
14349                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14350                      *  we should expand the list on onLoad
14351                      *  so command out it
14352                      */
14353 //                    this.expand();
14354                 }
14355             }else{
14356                 this.selectedIndex = -1;
14357                 this.onLoad();   
14358             }
14359         }
14360         
14361         this.loadNext = false;
14362     },
14363     
14364     // private
14365     getParams : function(q){
14366         var p = {};
14367         //p[this.queryParam] = q;
14368         
14369         if(this.pageSize){
14370             p.start = 0;
14371             p.limit = this.pageSize;
14372         }
14373         return p;
14374     },
14375
14376     /**
14377      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14378      */
14379     collapse : function(){
14380         if(!this.isExpanded()){
14381             return;
14382         }
14383         
14384         this.list.hide();
14385         
14386         this.hasFocus = false;
14387         
14388         if(this.tickable){
14389             this.okBtn.hide();
14390             this.cancelBtn.hide();
14391             this.trigger.show();
14392             
14393             if(this.editable){
14394                 this.tickableInputEl().dom.value = '';
14395                 this.tickableInputEl().blur();
14396             }
14397             
14398         }
14399         
14400         Roo.get(document).un('mousedown', this.collapseIf, this);
14401         Roo.get(document).un('mousewheel', this.collapseIf, this);
14402         if (!this.editable) {
14403             Roo.get(document).un('keydown', this.listKeyPress, this);
14404         }
14405         this.fireEvent('collapse', this);
14406         
14407         this.validate();
14408     },
14409
14410     // private
14411     collapseIf : function(e){
14412         var in_combo  = e.within(this.el);
14413         var in_list =  e.within(this.list);
14414         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14415         
14416         if (in_combo || in_list || is_list) {
14417             //e.stopPropagation();
14418             return;
14419         }
14420         
14421         if(this.tickable){
14422             this.onTickableFooterButtonClick(e, false, false);
14423         }
14424
14425         this.collapse();
14426         
14427     },
14428
14429     /**
14430      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14431      */
14432     expand : function(){
14433        
14434         if(this.isExpanded() || !this.hasFocus){
14435             return;
14436         }
14437         
14438         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14439         this.list.setWidth(lw);
14440         
14441         Roo.log('expand');
14442         
14443         this.list.show();
14444         
14445         this.restrictHeight();
14446         
14447         if(this.tickable){
14448             
14449             this.tickItems = Roo.apply([], this.item);
14450             
14451             this.okBtn.show();
14452             this.cancelBtn.show();
14453             this.trigger.hide();
14454             
14455             if(this.editable){
14456                 this.tickableInputEl().focus();
14457             }
14458             
14459         }
14460         
14461         Roo.get(document).on('mousedown', this.collapseIf, this);
14462         Roo.get(document).on('mousewheel', this.collapseIf, this);
14463         if (!this.editable) {
14464             Roo.get(document).on('keydown', this.listKeyPress, this);
14465         }
14466         
14467         this.fireEvent('expand', this);
14468     },
14469
14470     // private
14471     // Implements the default empty TriggerField.onTriggerClick function
14472     onTriggerClick : function(e)
14473     {
14474         Roo.log('trigger click');
14475         
14476         if(this.disabled || !this.triggerList){
14477             return;
14478         }
14479         
14480         this.page = 0;
14481         this.loadNext = false;
14482         
14483         if(this.isExpanded()){
14484             this.collapse();
14485             if (!this.blockFocus) {
14486                 this.inputEl().focus();
14487             }
14488             
14489         }else {
14490             this.hasFocus = true;
14491             if(this.triggerAction == 'all') {
14492                 this.doQuery(this.allQuery, true);
14493             } else {
14494                 this.doQuery(this.getRawValue());
14495             }
14496             if (!this.blockFocus) {
14497                 this.inputEl().focus();
14498             }
14499         }
14500     },
14501     
14502     onTickableTriggerClick : function(e)
14503     {
14504         if(this.disabled){
14505             return;
14506         }
14507         
14508         this.page = 0;
14509         this.loadNext = false;
14510         this.hasFocus = true;
14511         
14512         if(this.triggerAction == 'all') {
14513             this.doQuery(this.allQuery, true);
14514         } else {
14515             this.doQuery(this.getRawValue());
14516         }
14517     },
14518     
14519     onSearchFieldClick : function(e)
14520     {
14521         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14522             this.onTickableFooterButtonClick(e, false, false);
14523             return;
14524         }
14525         
14526         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14527             return;
14528         }
14529         
14530         this.page = 0;
14531         this.loadNext = false;
14532         this.hasFocus = true;
14533         
14534         if(this.triggerAction == 'all') {
14535             this.doQuery(this.allQuery, true);
14536         } else {
14537             this.doQuery(this.getRawValue());
14538         }
14539     },
14540     
14541     listKeyPress : function(e)
14542     {
14543         //Roo.log('listkeypress');
14544         // scroll to first matching element based on key pres..
14545         if (e.isSpecialKey()) {
14546             return false;
14547         }
14548         var k = String.fromCharCode(e.getKey()).toUpperCase();
14549         //Roo.log(k);
14550         var match  = false;
14551         var csel = this.view.getSelectedNodes();
14552         var cselitem = false;
14553         if (csel.length) {
14554             var ix = this.view.indexOf(csel[0]);
14555             cselitem  = this.store.getAt(ix);
14556             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14557                 cselitem = false;
14558             }
14559             
14560         }
14561         
14562         this.store.each(function(v) { 
14563             if (cselitem) {
14564                 // start at existing selection.
14565                 if (cselitem.id == v.id) {
14566                     cselitem = false;
14567                 }
14568                 return true;
14569             }
14570                 
14571             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14572                 match = this.store.indexOf(v);
14573                 return false;
14574             }
14575             return true;
14576         }, this);
14577         
14578         if (match === false) {
14579             return true; // no more action?
14580         }
14581         // scroll to?
14582         this.view.select(match);
14583         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14584         sn.scrollIntoView(sn.dom.parentNode, false);
14585     },
14586     
14587     onViewScroll : function(e, t){
14588         
14589         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){
14590             return;
14591         }
14592         
14593         this.hasQuery = true;
14594         
14595         this.loading = this.list.select('.loading', true).first();
14596         
14597         if(this.loading === null){
14598             this.list.createChild({
14599                 tag: 'div',
14600                 cls: 'loading roo-select2-more-results roo-select2-active',
14601                 html: 'Loading more results...'
14602             });
14603             
14604             this.loading = this.list.select('.loading', true).first();
14605             
14606             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14607             
14608             this.loading.hide();
14609         }
14610         
14611         this.loading.show();
14612         
14613         var _combo = this;
14614         
14615         this.page++;
14616         this.loadNext = true;
14617         
14618         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14619         
14620         return;
14621     },
14622     
14623     addItem : function(o)
14624     {   
14625         var dv = ''; // display value
14626         
14627         if (this.displayField) {
14628             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14629         } else {
14630             // this is an error condition!!!
14631             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14632         }
14633         
14634         if(!dv.length){
14635             return;
14636         }
14637         
14638         var choice = this.choices.createChild({
14639             tag: 'li',
14640             cls: 'roo-select2-search-choice',
14641             cn: [
14642                 {
14643                     tag: 'div',
14644                     html: dv
14645                 },
14646                 {
14647                     tag: 'a',
14648                     href: '#',
14649                     cls: 'roo-select2-search-choice-close fa fa-times',
14650                     tabindex: '-1'
14651                 }
14652             ]
14653             
14654         }, this.searchField);
14655         
14656         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14657         
14658         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14659         
14660         this.item.push(o);
14661         
14662         this.lastData = o;
14663         
14664         this.syncValue();
14665         
14666         this.inputEl().dom.value = '';
14667         
14668         this.validate();
14669     },
14670     
14671     onRemoveItem : function(e, _self, o)
14672     {
14673         e.preventDefault();
14674         
14675         this.lastItem = Roo.apply([], this.item);
14676         
14677         var index = this.item.indexOf(o.data) * 1;
14678         
14679         if( index < 0){
14680             Roo.log('not this item?!');
14681             return;
14682         }
14683         
14684         this.item.splice(index, 1);
14685         o.item.remove();
14686         
14687         this.syncValue();
14688         
14689         this.fireEvent('remove', this, e);
14690         
14691         this.validate();
14692         
14693     },
14694     
14695     syncValue : function()
14696     {
14697         if(!this.item.length){
14698             this.clearValue();
14699             return;
14700         }
14701             
14702         var value = [];
14703         var _this = this;
14704         Roo.each(this.item, function(i){
14705             if(_this.valueField){
14706                 value.push(i[_this.valueField]);
14707                 return;
14708             }
14709
14710             value.push(i);
14711         });
14712
14713         this.value = value.join(',');
14714
14715         if(this.hiddenField){
14716             this.hiddenField.dom.value = this.value;
14717         }
14718         
14719         this.store.fireEvent("datachanged", this.store);
14720         
14721         this.validate();
14722     },
14723     
14724     clearItem : function()
14725     {
14726         if(!this.multiple){
14727             return;
14728         }
14729         
14730         this.item = [];
14731         
14732         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14733            c.remove();
14734         });
14735         
14736         this.syncValue();
14737         
14738         this.validate();
14739         
14740         if(this.tickable && !Roo.isTouch){
14741             this.view.refresh();
14742         }
14743     },
14744     
14745     inputEl: function ()
14746     {
14747         if(Roo.isIOS && this.useNativeIOS){
14748             return this.el.select('select.roo-ios-select', true).first();
14749         }
14750         
14751         if(Roo.isTouch && this.mobileTouchView){
14752             return this.el.select('input.form-control',true).first();
14753         }
14754         
14755         if(this.tickable){
14756             return this.searchField;
14757         }
14758         
14759         return this.el.select('input.form-control',true).first();
14760     },
14761     
14762     onTickableFooterButtonClick : function(e, btn, el)
14763     {
14764         e.preventDefault();
14765         
14766         this.lastItem = Roo.apply([], this.item);
14767         
14768         if(btn && btn.name == 'cancel'){
14769             this.tickItems = Roo.apply([], this.item);
14770             this.collapse();
14771             return;
14772         }
14773         
14774         this.clearItem();
14775         
14776         var _this = this;
14777         
14778         Roo.each(this.tickItems, function(o){
14779             _this.addItem(o);
14780         });
14781         
14782         this.collapse();
14783         
14784     },
14785     
14786     validate : function()
14787     {
14788         if(this.getVisibilityEl().hasClass('hidden')){
14789             return true;
14790         }
14791         
14792         var v = this.getRawValue();
14793         
14794         if(this.multiple){
14795             v = this.getValue();
14796         }
14797         
14798         if(this.disabled || this.allowBlank || v.length){
14799             this.markValid();
14800             return true;
14801         }
14802         
14803         this.markInvalid();
14804         return false;
14805     },
14806     
14807     tickableInputEl : function()
14808     {
14809         if(!this.tickable || !this.editable){
14810             return this.inputEl();
14811         }
14812         
14813         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14814     },
14815     
14816     
14817     getAutoCreateTouchView : function()
14818     {
14819         var id = Roo.id();
14820         
14821         var cfg = {
14822             cls: 'form-group' //input-group
14823         };
14824         
14825         var input =  {
14826             tag: 'input',
14827             id : id,
14828             type : this.inputType,
14829             cls : 'form-control x-combo-noedit',
14830             autocomplete: 'new-password',
14831             placeholder : this.placeholder || '',
14832             readonly : true
14833         };
14834         
14835         if (this.name) {
14836             input.name = this.name;
14837         }
14838         
14839         if (this.size) {
14840             input.cls += ' input-' + this.size;
14841         }
14842         
14843         if (this.disabled) {
14844             input.disabled = true;
14845         }
14846         
14847         var inputblock = {
14848             cls : '',
14849             cn : [
14850                 input
14851             ]
14852         };
14853         
14854         if(this.before){
14855             inputblock.cls += ' input-group';
14856             
14857             inputblock.cn.unshift({
14858                 tag :'span',
14859                 cls : 'input-group-addon',
14860                 html : this.before
14861             });
14862         }
14863         
14864         if(this.removable && !this.multiple){
14865             inputblock.cls += ' roo-removable';
14866             
14867             inputblock.cn.push({
14868                 tag: 'button',
14869                 html : 'x',
14870                 cls : 'roo-combo-removable-btn close'
14871             });
14872         }
14873
14874         if(this.hasFeedback && !this.allowBlank){
14875             
14876             inputblock.cls += ' has-feedback';
14877             
14878             inputblock.cn.push({
14879                 tag: 'span',
14880                 cls: 'glyphicon form-control-feedback'
14881             });
14882             
14883         }
14884         
14885         if (this.after) {
14886             
14887             inputblock.cls += (this.before) ? '' : ' input-group';
14888             
14889             inputblock.cn.push({
14890                 tag :'span',
14891                 cls : 'input-group-addon',
14892                 html : this.after
14893             });
14894         }
14895
14896         var box = {
14897             tag: 'div',
14898             cn: [
14899                 {
14900                     tag: 'input',
14901                     type : 'hidden',
14902                     cls: 'form-hidden-field'
14903                 },
14904                 inputblock
14905             ]
14906             
14907         };
14908         
14909         if(this.multiple){
14910             box = {
14911                 tag: 'div',
14912                 cn: [
14913                     {
14914                         tag: 'input',
14915                         type : 'hidden',
14916                         cls: 'form-hidden-field'
14917                     },
14918                     {
14919                         tag: 'ul',
14920                         cls: 'roo-select2-choices',
14921                         cn:[
14922                             {
14923                                 tag: 'li',
14924                                 cls: 'roo-select2-search-field',
14925                                 cn: [
14926
14927                                     inputblock
14928                                 ]
14929                             }
14930                         ]
14931                     }
14932                 ]
14933             }
14934         };
14935         
14936         var combobox = {
14937             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14938             cn: [
14939                 box
14940             ]
14941         };
14942         
14943         if(!this.multiple && this.showToggleBtn){
14944             
14945             var caret = {
14946                         tag: 'span',
14947                         cls: 'caret'
14948             };
14949             
14950             if (this.caret != false) {
14951                 caret = {
14952                      tag: 'i',
14953                      cls: 'fa fa-' + this.caret
14954                 };
14955                 
14956             }
14957             
14958             combobox.cn.push({
14959                 tag :'span',
14960                 cls : 'input-group-addon btn dropdown-toggle',
14961                 cn : [
14962                     caret,
14963                     {
14964                         tag: 'span',
14965                         cls: 'combobox-clear',
14966                         cn  : [
14967                             {
14968                                 tag : 'i',
14969                                 cls: 'icon-remove'
14970                             }
14971                         ]
14972                     }
14973                 ]
14974
14975             })
14976         }
14977         
14978         if(this.multiple){
14979             combobox.cls += ' roo-select2-container-multi';
14980         }
14981         
14982         var align = this.labelAlign || this.parentLabelAlign();
14983         
14984         if (align ==='left' && this.fieldLabel.length) {
14985
14986             cfg.cn = [
14987                 {
14988                    tag : 'i',
14989                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14990                    tooltip : 'This field is required'
14991                 },
14992                 {
14993                     tag: 'label',
14994                     cls : 'control-label',
14995                     html : this.fieldLabel
14996
14997                 },
14998                 {
14999                     cls : '', 
15000                     cn: [
15001                         combobox
15002                     ]
15003                 }
15004             ];
15005             
15006             var labelCfg = cfg.cn[1];
15007             var contentCfg = cfg.cn[2];
15008             
15009
15010             if(this.indicatorpos == 'right'){
15011                 cfg.cn = [
15012                     {
15013                         tag: 'label',
15014                         'for' :  id,
15015                         cls : 'control-label',
15016                         cn : [
15017                             {
15018                                 tag : 'span',
15019                                 html : this.fieldLabel
15020                             },
15021                             {
15022                                 tag : 'i',
15023                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15024                                 tooltip : 'This field is required'
15025                             }
15026                         ]
15027                     },
15028                     {
15029                         cls : "",
15030                         cn: [
15031                             combobox
15032                         ]
15033                     }
15034
15035                 ];
15036                 
15037                 labelCfg = cfg.cn[0];
15038                 contentCfg = cfg.cn[1];
15039             }
15040             
15041            
15042             
15043             if(this.labelWidth > 12){
15044                 labelCfg.style = "width: " + this.labelWidth + 'px';
15045             }
15046             
15047             if(this.labelWidth < 13 && this.labelmd == 0){
15048                 this.labelmd = this.labelWidth;
15049             }
15050             
15051             if(this.labellg > 0){
15052                 labelCfg.cls += ' col-lg-' + this.labellg;
15053                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15054             }
15055             
15056             if(this.labelmd > 0){
15057                 labelCfg.cls += ' col-md-' + this.labelmd;
15058                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15059             }
15060             
15061             if(this.labelsm > 0){
15062                 labelCfg.cls += ' col-sm-' + this.labelsm;
15063                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15064             }
15065             
15066             if(this.labelxs > 0){
15067                 labelCfg.cls += ' col-xs-' + this.labelxs;
15068                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15069             }
15070                 
15071                 
15072         } else if ( this.fieldLabel.length) {
15073             cfg.cn = [
15074                 {
15075                    tag : 'i',
15076                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15077                    tooltip : 'This field is required'
15078                 },
15079                 {
15080                     tag: 'label',
15081                     cls : 'control-label',
15082                     html : this.fieldLabel
15083
15084                 },
15085                 {
15086                     cls : '', 
15087                     cn: [
15088                         combobox
15089                     ]
15090                 }
15091             ];
15092             
15093             if(this.indicatorpos == 'right'){
15094                 cfg.cn = [
15095                     {
15096                         tag: 'label',
15097                         cls : 'control-label',
15098                         html : this.fieldLabel,
15099                         cn : [
15100                             {
15101                                tag : 'i',
15102                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15103                                tooltip : 'This field is required'
15104                             }
15105                         ]
15106                     },
15107                     {
15108                         cls : '', 
15109                         cn: [
15110                             combobox
15111                         ]
15112                     }
15113                 ];
15114             }
15115         } else {
15116             cfg.cn = combobox;    
15117         }
15118         
15119         
15120         var settings = this;
15121         
15122         ['xs','sm','md','lg'].map(function(size){
15123             if (settings[size]) {
15124                 cfg.cls += ' col-' + size + '-' + settings[size];
15125             }
15126         });
15127         
15128         return cfg;
15129     },
15130     
15131     initTouchView : function()
15132     {
15133         this.renderTouchView();
15134         
15135         this.touchViewEl.on('scroll', function(){
15136             this.el.dom.scrollTop = 0;
15137         }, this);
15138         
15139         this.originalValue = this.getValue();
15140         
15141         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15142         
15143         this.inputEl().on("click", this.showTouchView, this);
15144         if (this.triggerEl) {
15145             this.triggerEl.on("click", this.showTouchView, this);
15146         }
15147         
15148         
15149         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15150         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15151         
15152         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15153         
15154         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15155         this.store.on('load', this.onTouchViewLoad, this);
15156         this.store.on('loadexception', this.onTouchViewLoadException, this);
15157         
15158         if(this.hiddenName){
15159             
15160             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15161             
15162             this.hiddenField.dom.value =
15163                 this.hiddenValue !== undefined ? this.hiddenValue :
15164                 this.value !== undefined ? this.value : '';
15165         
15166             this.el.dom.removeAttribute('name');
15167             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15168         }
15169         
15170         if(this.multiple){
15171             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15172             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15173         }
15174         
15175         if(this.removable && !this.multiple){
15176             var close = this.closeTriggerEl();
15177             if(close){
15178                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15179                 close.on('click', this.removeBtnClick, this, close);
15180             }
15181         }
15182         /*
15183          * fix the bug in Safari iOS8
15184          */
15185         this.inputEl().on("focus", function(e){
15186             document.activeElement.blur();
15187         }, this);
15188         
15189         return;
15190         
15191         
15192     },
15193     
15194     renderTouchView : function()
15195     {
15196         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15197         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15198         
15199         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15200         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15201         
15202         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15203         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15204         this.touchViewBodyEl.setStyle('overflow', 'auto');
15205         
15206         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15207         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15208         
15209         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15210         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15211         
15212     },
15213     
15214     showTouchView : function()
15215     {
15216         if(this.disabled){
15217             return;
15218         }
15219         
15220         this.touchViewHeaderEl.hide();
15221
15222         if(this.modalTitle.length){
15223             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15224             this.touchViewHeaderEl.show();
15225         }
15226
15227         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15228         this.touchViewEl.show();
15229
15230         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15231         
15232         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15233         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15234
15235         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15236
15237         if(this.modalTitle.length){
15238             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15239         }
15240         
15241         this.touchViewBodyEl.setHeight(bodyHeight);
15242
15243         if(this.animate){
15244             var _this = this;
15245             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15246         }else{
15247             this.touchViewEl.addClass('in');
15248         }
15249
15250         this.doTouchViewQuery();
15251         
15252     },
15253     
15254     hideTouchView : function()
15255     {
15256         this.touchViewEl.removeClass('in');
15257
15258         if(this.animate){
15259             var _this = this;
15260             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15261         }else{
15262             this.touchViewEl.setStyle('display', 'none');
15263         }
15264         
15265     },
15266     
15267     setTouchViewValue : function()
15268     {
15269         if(this.multiple){
15270             this.clearItem();
15271         
15272             var _this = this;
15273
15274             Roo.each(this.tickItems, function(o){
15275                 this.addItem(o);
15276             }, this);
15277         }
15278         
15279         this.hideTouchView();
15280     },
15281     
15282     doTouchViewQuery : function()
15283     {
15284         var qe = {
15285             query: '',
15286             forceAll: true,
15287             combo: this,
15288             cancel:false
15289         };
15290         
15291         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15292             return false;
15293         }
15294         
15295         if(!this.alwaysQuery || this.mode == 'local'){
15296             this.onTouchViewLoad();
15297             return;
15298         }
15299         
15300         this.store.load();
15301     },
15302     
15303     onTouchViewBeforeLoad : function(combo,opts)
15304     {
15305         return;
15306     },
15307
15308     // private
15309     onTouchViewLoad : function()
15310     {
15311         if(this.store.getCount() < 1){
15312             this.onTouchViewEmptyResults();
15313             return;
15314         }
15315         
15316         this.clearTouchView();
15317         
15318         var rawValue = this.getRawValue();
15319         
15320         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15321         
15322         this.tickItems = [];
15323         
15324         this.store.data.each(function(d, rowIndex){
15325             var row = this.touchViewListGroup.createChild(template);
15326             
15327             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15328                 row.addClass(d.data.cls);
15329             }
15330             
15331             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15332                 var cfg = {
15333                     data : d.data,
15334                     html : d.data[this.displayField]
15335                 };
15336                 
15337                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15338                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15339                 }
15340             }
15341             row.removeClass('selected');
15342             if(!this.multiple && this.valueField &&
15343                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15344             {
15345                 // radio buttons..
15346                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15347                 row.addClass('selected');
15348             }
15349             
15350             if(this.multiple && this.valueField &&
15351                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15352             {
15353                 
15354                 // checkboxes...
15355                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15356                 this.tickItems.push(d.data);
15357             }
15358             
15359             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15360             
15361         }, this);
15362         
15363         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15364         
15365         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15366
15367         if(this.modalTitle.length){
15368             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15369         }
15370
15371         var listHeight = this.touchViewListGroup.getHeight();
15372         
15373         var _this = this;
15374         
15375         if(firstChecked && listHeight > bodyHeight){
15376             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15377         }
15378         
15379     },
15380     
15381     onTouchViewLoadException : function()
15382     {
15383         this.hideTouchView();
15384     },
15385     
15386     onTouchViewEmptyResults : function()
15387     {
15388         this.clearTouchView();
15389         
15390         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15391         
15392         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15393         
15394     },
15395     
15396     clearTouchView : function()
15397     {
15398         this.touchViewListGroup.dom.innerHTML = '';
15399     },
15400     
15401     onTouchViewClick : function(e, el, o)
15402     {
15403         e.preventDefault();
15404         
15405         var row = o.row;
15406         var rowIndex = o.rowIndex;
15407         
15408         var r = this.store.getAt(rowIndex);
15409         
15410         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15411             
15412             if(!this.multiple){
15413                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15414                     c.dom.removeAttribute('checked');
15415                 }, this);
15416
15417                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15418
15419                 this.setFromData(r.data);
15420
15421                 var close = this.closeTriggerEl();
15422
15423                 if(close){
15424                     close.show();
15425                 }
15426
15427                 this.hideTouchView();
15428
15429                 this.fireEvent('select', this, r, rowIndex);
15430
15431                 return;
15432             }
15433
15434             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15435                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15436                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15437                 return;
15438             }
15439
15440             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15441             this.addItem(r.data);
15442             this.tickItems.push(r.data);
15443         }
15444     },
15445     
15446     getAutoCreateNativeIOS : function()
15447     {
15448         var cfg = {
15449             cls: 'form-group' //input-group,
15450         };
15451         
15452         var combobox =  {
15453             tag: 'select',
15454             cls : 'roo-ios-select'
15455         };
15456         
15457         if (this.name) {
15458             combobox.name = this.name;
15459         }
15460         
15461         if (this.disabled) {
15462             combobox.disabled = true;
15463         }
15464         
15465         var settings = this;
15466         
15467         ['xs','sm','md','lg'].map(function(size){
15468             if (settings[size]) {
15469                 cfg.cls += ' col-' + size + '-' + settings[size];
15470             }
15471         });
15472         
15473         cfg.cn = combobox;
15474         
15475         return cfg;
15476         
15477     },
15478     
15479     initIOSView : function()
15480     {
15481         this.store.on('load', this.onIOSViewLoad, this);
15482         
15483         return;
15484     },
15485     
15486     onIOSViewLoad : function()
15487     {
15488         if(this.store.getCount() < 1){
15489             return;
15490         }
15491         
15492         this.clearIOSView();
15493         
15494         if(this.allowBlank) {
15495             
15496             var default_text = '-- SELECT --';
15497             
15498             if(this.placeholder.length){
15499                 default_text = this.placeholder;
15500             }
15501             
15502             if(this.emptyTitle.length){
15503                 default_text += ' - ' + this.emptyTitle + ' -';
15504             }
15505             
15506             var opt = this.inputEl().createChild({
15507                 tag: 'option',
15508                 value : 0,
15509                 html : default_text
15510             });
15511             
15512             var o = {};
15513             o[this.valueField] = 0;
15514             o[this.displayField] = default_text;
15515             
15516             this.ios_options.push({
15517                 data : o,
15518                 el : opt
15519             });
15520             
15521         }
15522         
15523         this.store.data.each(function(d, rowIndex){
15524             
15525             var html = '';
15526             
15527             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15528                 html = d.data[this.displayField];
15529             }
15530             
15531             var value = '';
15532             
15533             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15534                 value = d.data[this.valueField];
15535             }
15536             
15537             var option = {
15538                 tag: 'option',
15539                 value : value,
15540                 html : html
15541             };
15542             
15543             if(this.value == d.data[this.valueField]){
15544                 option['selected'] = true;
15545             }
15546             
15547             var opt = this.inputEl().createChild(option);
15548             
15549             this.ios_options.push({
15550                 data : d.data,
15551                 el : opt
15552             });
15553             
15554         }, this);
15555         
15556         this.inputEl().on('change', function(){
15557            this.fireEvent('select', this);
15558         }, this);
15559         
15560     },
15561     
15562     clearIOSView: function()
15563     {
15564         this.inputEl().dom.innerHTML = '';
15565         
15566         this.ios_options = [];
15567     },
15568     
15569     setIOSValue: function(v)
15570     {
15571         this.value = v;
15572         
15573         if(!this.ios_options){
15574             return;
15575         }
15576         
15577         Roo.each(this.ios_options, function(opts){
15578            
15579            opts.el.dom.removeAttribute('selected');
15580            
15581            if(opts.data[this.valueField] != v){
15582                return;
15583            }
15584            
15585            opts.el.dom.setAttribute('selected', true);
15586            
15587         }, this);
15588     }
15589
15590     /** 
15591     * @cfg {Boolean} grow 
15592     * @hide 
15593     */
15594     /** 
15595     * @cfg {Number} growMin 
15596     * @hide 
15597     */
15598     /** 
15599     * @cfg {Number} growMax 
15600     * @hide 
15601     */
15602     /**
15603      * @hide
15604      * @method autoSize
15605      */
15606 });
15607
15608 Roo.apply(Roo.bootstrap.ComboBox,  {
15609     
15610     header : {
15611         tag: 'div',
15612         cls: 'modal-header',
15613         cn: [
15614             {
15615                 tag: 'h4',
15616                 cls: 'modal-title'
15617             }
15618         ]
15619     },
15620     
15621     body : {
15622         tag: 'div',
15623         cls: 'modal-body',
15624         cn: [
15625             {
15626                 tag: 'ul',
15627                 cls: 'list-group'
15628             }
15629         ]
15630     },
15631     
15632     listItemRadio : {
15633         tag: 'li',
15634         cls: 'list-group-item',
15635         cn: [
15636             {
15637                 tag: 'span',
15638                 cls: 'roo-combobox-list-group-item-value'
15639             },
15640             {
15641                 tag: 'div',
15642                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15643                 cn: [
15644                     {
15645                         tag: 'input',
15646                         type: 'radio'
15647                     },
15648                     {
15649                         tag: 'label'
15650                     }
15651                 ]
15652             }
15653         ]
15654     },
15655     
15656     listItemCheckbox : {
15657         tag: 'li',
15658         cls: 'list-group-item',
15659         cn: [
15660             {
15661                 tag: 'span',
15662                 cls: 'roo-combobox-list-group-item-value'
15663             },
15664             {
15665                 tag: 'div',
15666                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15667                 cn: [
15668                     {
15669                         tag: 'input',
15670                         type: 'checkbox'
15671                     },
15672                     {
15673                         tag: 'label'
15674                     }
15675                 ]
15676             }
15677         ]
15678     },
15679     
15680     emptyResult : {
15681         tag: 'div',
15682         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15683     },
15684     
15685     footer : {
15686         tag: 'div',
15687         cls: 'modal-footer',
15688         cn: [
15689             {
15690                 tag: 'div',
15691                 cls: 'row',
15692                 cn: [
15693                     {
15694                         tag: 'div',
15695                         cls: 'col-xs-6 text-left',
15696                         cn: {
15697                             tag: 'button',
15698                             cls: 'btn btn-danger roo-touch-view-cancel',
15699                             html: 'Cancel'
15700                         }
15701                     },
15702                     {
15703                         tag: 'div',
15704                         cls: 'col-xs-6 text-right',
15705                         cn: {
15706                             tag: 'button',
15707                             cls: 'btn btn-success roo-touch-view-ok',
15708                             html: 'OK'
15709                         }
15710                     }
15711                 ]
15712             }
15713         ]
15714         
15715     }
15716 });
15717
15718 Roo.apply(Roo.bootstrap.ComboBox,  {
15719     
15720     touchViewTemplate : {
15721         tag: 'div',
15722         cls: 'modal fade roo-combobox-touch-view',
15723         cn: [
15724             {
15725                 tag: 'div',
15726                 cls: 'modal-dialog',
15727                 style : 'position:fixed', // we have to fix position....
15728                 cn: [
15729                     {
15730                         tag: 'div',
15731                         cls: 'modal-content',
15732                         cn: [
15733                             Roo.bootstrap.ComboBox.header,
15734                             Roo.bootstrap.ComboBox.body,
15735                             Roo.bootstrap.ComboBox.footer
15736                         ]
15737                     }
15738                 ]
15739             }
15740         ]
15741     }
15742 });/*
15743  * Based on:
15744  * Ext JS Library 1.1.1
15745  * Copyright(c) 2006-2007, Ext JS, LLC.
15746  *
15747  * Originally Released Under LGPL - original licence link has changed is not relivant.
15748  *
15749  * Fork - LGPL
15750  * <script type="text/javascript">
15751  */
15752
15753 /**
15754  * @class Roo.View
15755  * @extends Roo.util.Observable
15756  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15757  * This class also supports single and multi selection modes. <br>
15758  * Create a data model bound view:
15759  <pre><code>
15760  var store = new Roo.data.Store(...);
15761
15762  var view = new Roo.View({
15763     el : "my-element",
15764     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15765  
15766     singleSelect: true,
15767     selectedClass: "ydataview-selected",
15768     store: store
15769  });
15770
15771  // listen for node click?
15772  view.on("click", function(vw, index, node, e){
15773  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15774  });
15775
15776  // load XML data
15777  dataModel.load("foobar.xml");
15778  </code></pre>
15779  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15780  * <br><br>
15781  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15782  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15783  * 
15784  * Note: old style constructor is still suported (container, template, config)
15785  * 
15786  * @constructor
15787  * Create a new View
15788  * @param {Object} config The config object
15789  * 
15790  */
15791 Roo.View = function(config, depreciated_tpl, depreciated_config){
15792     
15793     this.parent = false;
15794     
15795     if (typeof(depreciated_tpl) == 'undefined') {
15796         // new way.. - universal constructor.
15797         Roo.apply(this, config);
15798         this.el  = Roo.get(this.el);
15799     } else {
15800         // old format..
15801         this.el  = Roo.get(config);
15802         this.tpl = depreciated_tpl;
15803         Roo.apply(this, depreciated_config);
15804     }
15805     this.wrapEl  = this.el.wrap().wrap();
15806     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15807     
15808     
15809     if(typeof(this.tpl) == "string"){
15810         this.tpl = new Roo.Template(this.tpl);
15811     } else {
15812         // support xtype ctors..
15813         this.tpl = new Roo.factory(this.tpl, Roo);
15814     }
15815     
15816     
15817     this.tpl.compile();
15818     
15819     /** @private */
15820     this.addEvents({
15821         /**
15822          * @event beforeclick
15823          * Fires before a click is processed. Returns false to cancel the default action.
15824          * @param {Roo.View} this
15825          * @param {Number} index The index of the target node
15826          * @param {HTMLElement} node The target node
15827          * @param {Roo.EventObject} e The raw event object
15828          */
15829             "beforeclick" : true,
15830         /**
15831          * @event click
15832          * Fires when a template node is clicked.
15833          * @param {Roo.View} this
15834          * @param {Number} index The index of the target node
15835          * @param {HTMLElement} node The target node
15836          * @param {Roo.EventObject} e The raw event object
15837          */
15838             "click" : true,
15839         /**
15840          * @event dblclick
15841          * Fires when a template node is double clicked.
15842          * @param {Roo.View} this
15843          * @param {Number} index The index of the target node
15844          * @param {HTMLElement} node The target node
15845          * @param {Roo.EventObject} e The raw event object
15846          */
15847             "dblclick" : true,
15848         /**
15849          * @event contextmenu
15850          * Fires when a template node is right clicked.
15851          * @param {Roo.View} this
15852          * @param {Number} index The index of the target node
15853          * @param {HTMLElement} node The target node
15854          * @param {Roo.EventObject} e The raw event object
15855          */
15856             "contextmenu" : true,
15857         /**
15858          * @event selectionchange
15859          * Fires when the selected nodes change.
15860          * @param {Roo.View} this
15861          * @param {Array} selections Array of the selected nodes
15862          */
15863             "selectionchange" : true,
15864     
15865         /**
15866          * @event beforeselect
15867          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15868          * @param {Roo.View} this
15869          * @param {HTMLElement} node The node to be selected
15870          * @param {Array} selections Array of currently selected nodes
15871          */
15872             "beforeselect" : true,
15873         /**
15874          * @event preparedata
15875          * Fires on every row to render, to allow you to change the data.
15876          * @param {Roo.View} this
15877          * @param {Object} data to be rendered (change this)
15878          */
15879           "preparedata" : true
15880           
15881           
15882         });
15883
15884
15885
15886     this.el.on({
15887         "click": this.onClick,
15888         "dblclick": this.onDblClick,
15889         "contextmenu": this.onContextMenu,
15890         scope:this
15891     });
15892
15893     this.selections = [];
15894     this.nodes = [];
15895     this.cmp = new Roo.CompositeElementLite([]);
15896     if(this.store){
15897         this.store = Roo.factory(this.store, Roo.data);
15898         this.setStore(this.store, true);
15899     }
15900     
15901     if ( this.footer && this.footer.xtype) {
15902            
15903          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15904         
15905         this.footer.dataSource = this.store;
15906         this.footer.container = fctr;
15907         this.footer = Roo.factory(this.footer, Roo);
15908         fctr.insertFirst(this.el);
15909         
15910         // this is a bit insane - as the paging toolbar seems to detach the el..
15911 //        dom.parentNode.parentNode.parentNode
15912          // they get detached?
15913     }
15914     
15915     
15916     Roo.View.superclass.constructor.call(this);
15917     
15918     
15919 };
15920
15921 Roo.extend(Roo.View, Roo.util.Observable, {
15922     
15923      /**
15924      * @cfg {Roo.data.Store} store Data store to load data from.
15925      */
15926     store : false,
15927     
15928     /**
15929      * @cfg {String|Roo.Element} el The container element.
15930      */
15931     el : '',
15932     
15933     /**
15934      * @cfg {String|Roo.Template} tpl The template used by this View 
15935      */
15936     tpl : false,
15937     /**
15938      * @cfg {String} dataName the named area of the template to use as the data area
15939      *                          Works with domtemplates roo-name="name"
15940      */
15941     dataName: false,
15942     /**
15943      * @cfg {String} selectedClass The css class to add to selected nodes
15944      */
15945     selectedClass : "x-view-selected",
15946      /**
15947      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15948      */
15949     emptyText : "",
15950     
15951     /**
15952      * @cfg {String} text to display on mask (default Loading)
15953      */
15954     mask : false,
15955     /**
15956      * @cfg {Boolean} multiSelect Allow multiple selection
15957      */
15958     multiSelect : false,
15959     /**
15960      * @cfg {Boolean} singleSelect Allow single selection
15961      */
15962     singleSelect:  false,
15963     
15964     /**
15965      * @cfg {Boolean} toggleSelect - selecting 
15966      */
15967     toggleSelect : false,
15968     
15969     /**
15970      * @cfg {Boolean} tickable - selecting 
15971      */
15972     tickable : false,
15973     
15974     /**
15975      * Returns the element this view is bound to.
15976      * @return {Roo.Element}
15977      */
15978     getEl : function(){
15979         return this.wrapEl;
15980     },
15981     
15982     
15983
15984     /**
15985      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15986      */
15987     refresh : function(){
15988         //Roo.log('refresh');
15989         var t = this.tpl;
15990         
15991         // if we are using something like 'domtemplate', then
15992         // the what gets used is:
15993         // t.applySubtemplate(NAME, data, wrapping data..)
15994         // the outer template then get' applied with
15995         //     the store 'extra data'
15996         // and the body get's added to the
15997         //      roo-name="data" node?
15998         //      <span class='roo-tpl-{name}'></span> ?????
15999         
16000         
16001         
16002         this.clearSelections();
16003         this.el.update("");
16004         var html = [];
16005         var records = this.store.getRange();
16006         if(records.length < 1) {
16007             
16008             // is this valid??  = should it render a template??
16009             
16010             this.el.update(this.emptyText);
16011             return;
16012         }
16013         var el = this.el;
16014         if (this.dataName) {
16015             this.el.update(t.apply(this.store.meta)); //????
16016             el = this.el.child('.roo-tpl-' + this.dataName);
16017         }
16018         
16019         for(var i = 0, len = records.length; i < len; i++){
16020             var data = this.prepareData(records[i].data, i, records[i]);
16021             this.fireEvent("preparedata", this, data, i, records[i]);
16022             
16023             var d = Roo.apply({}, data);
16024             
16025             if(this.tickable){
16026                 Roo.apply(d, {'roo-id' : Roo.id()});
16027                 
16028                 var _this = this;
16029             
16030                 Roo.each(this.parent.item, function(item){
16031                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16032                         return;
16033                     }
16034                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16035                 });
16036             }
16037             
16038             html[html.length] = Roo.util.Format.trim(
16039                 this.dataName ?
16040                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16041                     t.apply(d)
16042             );
16043         }
16044         
16045         
16046         
16047         el.update(html.join(""));
16048         this.nodes = el.dom.childNodes;
16049         this.updateIndexes(0);
16050     },
16051     
16052
16053     /**
16054      * Function to override to reformat the data that is sent to
16055      * the template for each node.
16056      * DEPRICATED - use the preparedata event handler.
16057      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16058      * a JSON object for an UpdateManager bound view).
16059      */
16060     prepareData : function(data, index, record)
16061     {
16062         this.fireEvent("preparedata", this, data, index, record);
16063         return data;
16064     },
16065
16066     onUpdate : function(ds, record){
16067         // Roo.log('on update');   
16068         this.clearSelections();
16069         var index = this.store.indexOf(record);
16070         var n = this.nodes[index];
16071         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16072         n.parentNode.removeChild(n);
16073         this.updateIndexes(index, index);
16074     },
16075
16076     
16077     
16078 // --------- FIXME     
16079     onAdd : function(ds, records, index)
16080     {
16081         //Roo.log(['on Add', ds, records, index] );        
16082         this.clearSelections();
16083         if(this.nodes.length == 0){
16084             this.refresh();
16085             return;
16086         }
16087         var n = this.nodes[index];
16088         for(var i = 0, len = records.length; i < len; i++){
16089             var d = this.prepareData(records[i].data, i, records[i]);
16090             if(n){
16091                 this.tpl.insertBefore(n, d);
16092             }else{
16093                 
16094                 this.tpl.append(this.el, d);
16095             }
16096         }
16097         this.updateIndexes(index);
16098     },
16099
16100     onRemove : function(ds, record, index){
16101        // Roo.log('onRemove');
16102         this.clearSelections();
16103         var el = this.dataName  ?
16104             this.el.child('.roo-tpl-' + this.dataName) :
16105             this.el; 
16106         
16107         el.dom.removeChild(this.nodes[index]);
16108         this.updateIndexes(index);
16109     },
16110
16111     /**
16112      * Refresh an individual node.
16113      * @param {Number} index
16114      */
16115     refreshNode : function(index){
16116         this.onUpdate(this.store, this.store.getAt(index));
16117     },
16118
16119     updateIndexes : function(startIndex, endIndex){
16120         var ns = this.nodes;
16121         startIndex = startIndex || 0;
16122         endIndex = endIndex || ns.length - 1;
16123         for(var i = startIndex; i <= endIndex; i++){
16124             ns[i].nodeIndex = i;
16125         }
16126     },
16127
16128     /**
16129      * Changes the data store this view uses and refresh the view.
16130      * @param {Store} store
16131      */
16132     setStore : function(store, initial){
16133         if(!initial && this.store){
16134             this.store.un("datachanged", this.refresh);
16135             this.store.un("add", this.onAdd);
16136             this.store.un("remove", this.onRemove);
16137             this.store.un("update", this.onUpdate);
16138             this.store.un("clear", this.refresh);
16139             this.store.un("beforeload", this.onBeforeLoad);
16140             this.store.un("load", this.onLoad);
16141             this.store.un("loadexception", this.onLoad);
16142         }
16143         if(store){
16144           
16145             store.on("datachanged", this.refresh, this);
16146             store.on("add", this.onAdd, this);
16147             store.on("remove", this.onRemove, this);
16148             store.on("update", this.onUpdate, this);
16149             store.on("clear", this.refresh, this);
16150             store.on("beforeload", this.onBeforeLoad, this);
16151             store.on("load", this.onLoad, this);
16152             store.on("loadexception", this.onLoad, this);
16153         }
16154         
16155         if(store){
16156             this.refresh();
16157         }
16158     },
16159     /**
16160      * onbeforeLoad - masks the loading area.
16161      *
16162      */
16163     onBeforeLoad : function(store,opts)
16164     {
16165          //Roo.log('onBeforeLoad');   
16166         if (!opts.add) {
16167             this.el.update("");
16168         }
16169         this.el.mask(this.mask ? this.mask : "Loading" ); 
16170     },
16171     onLoad : function ()
16172     {
16173         this.el.unmask();
16174     },
16175     
16176
16177     /**
16178      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16179      * @param {HTMLElement} node
16180      * @return {HTMLElement} The template node
16181      */
16182     findItemFromChild : function(node){
16183         var el = this.dataName  ?
16184             this.el.child('.roo-tpl-' + this.dataName,true) :
16185             this.el.dom; 
16186         
16187         if(!node || node.parentNode == el){
16188                     return node;
16189             }
16190             var p = node.parentNode;
16191             while(p && p != el){
16192             if(p.parentNode == el){
16193                 return p;
16194             }
16195             p = p.parentNode;
16196         }
16197             return null;
16198     },
16199
16200     /** @ignore */
16201     onClick : function(e){
16202         var item = this.findItemFromChild(e.getTarget());
16203         if(item){
16204             var index = this.indexOf(item);
16205             if(this.onItemClick(item, index, e) !== false){
16206                 this.fireEvent("click", this, index, item, e);
16207             }
16208         }else{
16209             this.clearSelections();
16210         }
16211     },
16212
16213     /** @ignore */
16214     onContextMenu : function(e){
16215         var item = this.findItemFromChild(e.getTarget());
16216         if(item){
16217             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16218         }
16219     },
16220
16221     /** @ignore */
16222     onDblClick : function(e){
16223         var item = this.findItemFromChild(e.getTarget());
16224         if(item){
16225             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16226         }
16227     },
16228
16229     onItemClick : function(item, index, e)
16230     {
16231         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16232             return false;
16233         }
16234         if (this.toggleSelect) {
16235             var m = this.isSelected(item) ? 'unselect' : 'select';
16236             //Roo.log(m);
16237             var _t = this;
16238             _t[m](item, true, false);
16239             return true;
16240         }
16241         if(this.multiSelect || this.singleSelect){
16242             if(this.multiSelect && e.shiftKey && this.lastSelection){
16243                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16244             }else{
16245                 this.select(item, this.multiSelect && e.ctrlKey);
16246                 this.lastSelection = item;
16247             }
16248             
16249             if(!this.tickable){
16250                 e.preventDefault();
16251             }
16252             
16253         }
16254         return true;
16255     },
16256
16257     /**
16258      * Get the number of selected nodes.
16259      * @return {Number}
16260      */
16261     getSelectionCount : function(){
16262         return this.selections.length;
16263     },
16264
16265     /**
16266      * Get the currently selected nodes.
16267      * @return {Array} An array of HTMLElements
16268      */
16269     getSelectedNodes : function(){
16270         return this.selections;
16271     },
16272
16273     /**
16274      * Get the indexes of the selected nodes.
16275      * @return {Array}
16276      */
16277     getSelectedIndexes : function(){
16278         var indexes = [], s = this.selections;
16279         for(var i = 0, len = s.length; i < len; i++){
16280             indexes.push(s[i].nodeIndex);
16281         }
16282         return indexes;
16283     },
16284
16285     /**
16286      * Clear all selections
16287      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16288      */
16289     clearSelections : function(suppressEvent){
16290         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16291             this.cmp.elements = this.selections;
16292             this.cmp.removeClass(this.selectedClass);
16293             this.selections = [];
16294             if(!suppressEvent){
16295                 this.fireEvent("selectionchange", this, this.selections);
16296             }
16297         }
16298     },
16299
16300     /**
16301      * Returns true if the passed node is selected
16302      * @param {HTMLElement/Number} node The node or node index
16303      * @return {Boolean}
16304      */
16305     isSelected : function(node){
16306         var s = this.selections;
16307         if(s.length < 1){
16308             return false;
16309         }
16310         node = this.getNode(node);
16311         return s.indexOf(node) !== -1;
16312     },
16313
16314     /**
16315      * Selects nodes.
16316      * @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
16317      * @param {Boolean} keepExisting (optional) true to keep existing selections
16318      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16319      */
16320     select : function(nodeInfo, keepExisting, suppressEvent){
16321         if(nodeInfo instanceof Array){
16322             if(!keepExisting){
16323                 this.clearSelections(true);
16324             }
16325             for(var i = 0, len = nodeInfo.length; i < len; i++){
16326                 this.select(nodeInfo[i], true, true);
16327             }
16328             return;
16329         } 
16330         var node = this.getNode(nodeInfo);
16331         if(!node || this.isSelected(node)){
16332             return; // already selected.
16333         }
16334         if(!keepExisting){
16335             this.clearSelections(true);
16336         }
16337         
16338         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16339             Roo.fly(node).addClass(this.selectedClass);
16340             this.selections.push(node);
16341             if(!suppressEvent){
16342                 this.fireEvent("selectionchange", this, this.selections);
16343             }
16344         }
16345         
16346         
16347     },
16348       /**
16349      * Unselects nodes.
16350      * @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
16351      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16352      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16353      */
16354     unselect : function(nodeInfo, keepExisting, suppressEvent)
16355     {
16356         if(nodeInfo instanceof Array){
16357             Roo.each(this.selections, function(s) {
16358                 this.unselect(s, nodeInfo);
16359             }, this);
16360             return;
16361         }
16362         var node = this.getNode(nodeInfo);
16363         if(!node || !this.isSelected(node)){
16364             //Roo.log("not selected");
16365             return; // not selected.
16366         }
16367         // fireevent???
16368         var ns = [];
16369         Roo.each(this.selections, function(s) {
16370             if (s == node ) {
16371                 Roo.fly(node).removeClass(this.selectedClass);
16372
16373                 return;
16374             }
16375             ns.push(s);
16376         },this);
16377         
16378         this.selections= ns;
16379         this.fireEvent("selectionchange", this, this.selections);
16380     },
16381
16382     /**
16383      * Gets a template node.
16384      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16385      * @return {HTMLElement} The node or null if it wasn't found
16386      */
16387     getNode : function(nodeInfo){
16388         if(typeof nodeInfo == "string"){
16389             return document.getElementById(nodeInfo);
16390         }else if(typeof nodeInfo == "number"){
16391             return this.nodes[nodeInfo];
16392         }
16393         return nodeInfo;
16394     },
16395
16396     /**
16397      * Gets a range template nodes.
16398      * @param {Number} startIndex
16399      * @param {Number} endIndex
16400      * @return {Array} An array of nodes
16401      */
16402     getNodes : function(start, end){
16403         var ns = this.nodes;
16404         start = start || 0;
16405         end = typeof end == "undefined" ? ns.length - 1 : end;
16406         var nodes = [];
16407         if(start <= end){
16408             for(var i = start; i <= end; i++){
16409                 nodes.push(ns[i]);
16410             }
16411         } else{
16412             for(var i = start; i >= end; i--){
16413                 nodes.push(ns[i]);
16414             }
16415         }
16416         return nodes;
16417     },
16418
16419     /**
16420      * Finds the index of the passed node
16421      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16422      * @return {Number} The index of the node or -1
16423      */
16424     indexOf : function(node){
16425         node = this.getNode(node);
16426         if(typeof node.nodeIndex == "number"){
16427             return node.nodeIndex;
16428         }
16429         var ns = this.nodes;
16430         for(var i = 0, len = ns.length; i < len; i++){
16431             if(ns[i] == node){
16432                 return i;
16433             }
16434         }
16435         return -1;
16436     }
16437 });
16438 /*
16439  * - LGPL
16440  *
16441  * based on jquery fullcalendar
16442  * 
16443  */
16444
16445 Roo.bootstrap = Roo.bootstrap || {};
16446 /**
16447  * @class Roo.bootstrap.Calendar
16448  * @extends Roo.bootstrap.Component
16449  * Bootstrap Calendar class
16450  * @cfg {Boolean} loadMask (true|false) default false
16451  * @cfg {Object} header generate the user specific header of the calendar, default false
16452
16453  * @constructor
16454  * Create a new Container
16455  * @param {Object} config The config object
16456  */
16457
16458
16459
16460 Roo.bootstrap.Calendar = function(config){
16461     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16462      this.addEvents({
16463         /**
16464              * @event select
16465              * Fires when a date is selected
16466              * @param {DatePicker} this
16467              * @param {Date} date The selected date
16468              */
16469         'select': true,
16470         /**
16471              * @event monthchange
16472              * Fires when the displayed month changes 
16473              * @param {DatePicker} this
16474              * @param {Date} date The selected month
16475              */
16476         'monthchange': true,
16477         /**
16478              * @event evententer
16479              * Fires when mouse over an event
16480              * @param {Calendar} this
16481              * @param {event} Event
16482              */
16483         'evententer': true,
16484         /**
16485              * @event eventleave
16486              * Fires when the mouse leaves an
16487              * @param {Calendar} this
16488              * @param {event}
16489              */
16490         'eventleave': true,
16491         /**
16492              * @event eventclick
16493              * Fires when the mouse click an
16494              * @param {Calendar} this
16495              * @param {event}
16496              */
16497         'eventclick': true
16498         
16499     });
16500
16501 };
16502
16503 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16504     
16505      /**
16506      * @cfg {Number} startDay
16507      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16508      */
16509     startDay : 0,
16510     
16511     loadMask : false,
16512     
16513     header : false,
16514       
16515     getAutoCreate : function(){
16516         
16517         
16518         var fc_button = function(name, corner, style, content ) {
16519             return Roo.apply({},{
16520                 tag : 'span',
16521                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16522                          (corner.length ?
16523                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16524                             ''
16525                         ),
16526                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16527                 unselectable: 'on'
16528             });
16529         };
16530         
16531         var header = {};
16532         
16533         if(!this.header){
16534             header = {
16535                 tag : 'table',
16536                 cls : 'fc-header',
16537                 style : 'width:100%',
16538                 cn : [
16539                     {
16540                         tag: 'tr',
16541                         cn : [
16542                             {
16543                                 tag : 'td',
16544                                 cls : 'fc-header-left',
16545                                 cn : [
16546                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16547                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16548                                     { tag: 'span', cls: 'fc-header-space' },
16549                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16550
16551
16552                                 ]
16553                             },
16554
16555                             {
16556                                 tag : 'td',
16557                                 cls : 'fc-header-center',
16558                                 cn : [
16559                                     {
16560                                         tag: 'span',
16561                                         cls: 'fc-header-title',
16562                                         cn : {
16563                                             tag: 'H2',
16564                                             html : 'month / year'
16565                                         }
16566                                     }
16567
16568                                 ]
16569                             },
16570                             {
16571                                 tag : 'td',
16572                                 cls : 'fc-header-right',
16573                                 cn : [
16574                               /*      fc_button('month', 'left', '', 'month' ),
16575                                     fc_button('week', '', '', 'week' ),
16576                                     fc_button('day', 'right', '', 'day' )
16577                                 */    
16578
16579                                 ]
16580                             }
16581
16582                         ]
16583                     }
16584                 ]
16585             };
16586         }
16587         
16588         header = this.header;
16589         
16590        
16591         var cal_heads = function() {
16592             var ret = [];
16593             // fixme - handle this.
16594             
16595             for (var i =0; i < Date.dayNames.length; i++) {
16596                 var d = Date.dayNames[i];
16597                 ret.push({
16598                     tag: 'th',
16599                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16600                     html : d.substring(0,3)
16601                 });
16602                 
16603             }
16604             ret[0].cls += ' fc-first';
16605             ret[6].cls += ' fc-last';
16606             return ret;
16607         };
16608         var cal_cell = function(n) {
16609             return  {
16610                 tag: 'td',
16611                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16612                 cn : [
16613                     {
16614                         cn : [
16615                             {
16616                                 cls: 'fc-day-number',
16617                                 html: 'D'
16618                             },
16619                             {
16620                                 cls: 'fc-day-content',
16621                              
16622                                 cn : [
16623                                      {
16624                                         style: 'position: relative;' // height: 17px;
16625                                     }
16626                                 ]
16627                             }
16628                             
16629                             
16630                         ]
16631                     }
16632                 ]
16633                 
16634             }
16635         };
16636         var cal_rows = function() {
16637             
16638             var ret = [];
16639             for (var r = 0; r < 6; r++) {
16640                 var row= {
16641                     tag : 'tr',
16642                     cls : 'fc-week',
16643                     cn : []
16644                 };
16645                 
16646                 for (var i =0; i < Date.dayNames.length; i++) {
16647                     var d = Date.dayNames[i];
16648                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16649
16650                 }
16651                 row.cn[0].cls+=' fc-first';
16652                 row.cn[0].cn[0].style = 'min-height:90px';
16653                 row.cn[6].cls+=' fc-last';
16654                 ret.push(row);
16655                 
16656             }
16657             ret[0].cls += ' fc-first';
16658             ret[4].cls += ' fc-prev-last';
16659             ret[5].cls += ' fc-last';
16660             return ret;
16661             
16662         };
16663         
16664         var cal_table = {
16665             tag: 'table',
16666             cls: 'fc-border-separate',
16667             style : 'width:100%',
16668             cellspacing  : 0,
16669             cn : [
16670                 { 
16671                     tag: 'thead',
16672                     cn : [
16673                         { 
16674                             tag: 'tr',
16675                             cls : 'fc-first fc-last',
16676                             cn : cal_heads()
16677                         }
16678                     ]
16679                 },
16680                 { 
16681                     tag: 'tbody',
16682                     cn : cal_rows()
16683                 }
16684                   
16685             ]
16686         };
16687          
16688          var cfg = {
16689             cls : 'fc fc-ltr',
16690             cn : [
16691                 header,
16692                 {
16693                     cls : 'fc-content',
16694                     style : "position: relative;",
16695                     cn : [
16696                         {
16697                             cls : 'fc-view fc-view-month fc-grid',
16698                             style : 'position: relative',
16699                             unselectable : 'on',
16700                             cn : [
16701                                 {
16702                                     cls : 'fc-event-container',
16703                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16704                                 },
16705                                 cal_table
16706                             ]
16707                         }
16708                     ]
16709     
16710                 }
16711            ] 
16712             
16713         };
16714         
16715          
16716         
16717         return cfg;
16718     },
16719     
16720     
16721     initEvents : function()
16722     {
16723         if(!this.store){
16724             throw "can not find store for calendar";
16725         }
16726         
16727         var mark = {
16728             tag: "div",
16729             cls:"x-dlg-mask",
16730             style: "text-align:center",
16731             cn: [
16732                 {
16733                     tag: "div",
16734                     style: "background-color:white;width:50%;margin:250 auto",
16735                     cn: [
16736                         {
16737                             tag: "img",
16738                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16739                         },
16740                         {
16741                             tag: "span",
16742                             html: "Loading"
16743                         }
16744                         
16745                     ]
16746                 }
16747             ]
16748         };
16749         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16750         
16751         var size = this.el.select('.fc-content', true).first().getSize();
16752         this.maskEl.setSize(size.width, size.height);
16753         this.maskEl.enableDisplayMode("block");
16754         if(!this.loadMask){
16755             this.maskEl.hide();
16756         }
16757         
16758         this.store = Roo.factory(this.store, Roo.data);
16759         this.store.on('load', this.onLoad, this);
16760         this.store.on('beforeload', this.onBeforeLoad, this);
16761         
16762         this.resize();
16763         
16764         this.cells = this.el.select('.fc-day',true);
16765         //Roo.log(this.cells);
16766         this.textNodes = this.el.query('.fc-day-number');
16767         this.cells.addClassOnOver('fc-state-hover');
16768         
16769         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16770         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16771         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16772         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16773         
16774         this.on('monthchange', this.onMonthChange, this);
16775         
16776         this.update(new Date().clearTime());
16777     },
16778     
16779     resize : function() {
16780         var sz  = this.el.getSize();
16781         
16782         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16783         this.el.select('.fc-day-content div',true).setHeight(34);
16784     },
16785     
16786     
16787     // private
16788     showPrevMonth : function(e){
16789         this.update(this.activeDate.add("mo", -1));
16790     },
16791     showToday : function(e){
16792         this.update(new Date().clearTime());
16793     },
16794     // private
16795     showNextMonth : function(e){
16796         this.update(this.activeDate.add("mo", 1));
16797     },
16798
16799     // private
16800     showPrevYear : function(){
16801         this.update(this.activeDate.add("y", -1));
16802     },
16803
16804     // private
16805     showNextYear : function(){
16806         this.update(this.activeDate.add("y", 1));
16807     },
16808
16809     
16810    // private
16811     update : function(date)
16812     {
16813         var vd = this.activeDate;
16814         this.activeDate = date;
16815 //        if(vd && this.el){
16816 //            var t = date.getTime();
16817 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16818 //                Roo.log('using add remove');
16819 //                
16820 //                this.fireEvent('monthchange', this, date);
16821 //                
16822 //                this.cells.removeClass("fc-state-highlight");
16823 //                this.cells.each(function(c){
16824 //                   if(c.dateValue == t){
16825 //                       c.addClass("fc-state-highlight");
16826 //                       setTimeout(function(){
16827 //                            try{c.dom.firstChild.focus();}catch(e){}
16828 //                       }, 50);
16829 //                       return false;
16830 //                   }
16831 //                   return true;
16832 //                });
16833 //                return;
16834 //            }
16835 //        }
16836         
16837         var days = date.getDaysInMonth();
16838         
16839         var firstOfMonth = date.getFirstDateOfMonth();
16840         var startingPos = firstOfMonth.getDay()-this.startDay;
16841         
16842         if(startingPos < this.startDay){
16843             startingPos += 7;
16844         }
16845         
16846         var pm = date.add(Date.MONTH, -1);
16847         var prevStart = pm.getDaysInMonth()-startingPos;
16848 //        
16849         this.cells = this.el.select('.fc-day',true);
16850         this.textNodes = this.el.query('.fc-day-number');
16851         this.cells.addClassOnOver('fc-state-hover');
16852         
16853         var cells = this.cells.elements;
16854         var textEls = this.textNodes;
16855         
16856         Roo.each(cells, function(cell){
16857             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16858         });
16859         
16860         days += startingPos;
16861
16862         // convert everything to numbers so it's fast
16863         var day = 86400000;
16864         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16865         //Roo.log(d);
16866         //Roo.log(pm);
16867         //Roo.log(prevStart);
16868         
16869         var today = new Date().clearTime().getTime();
16870         var sel = date.clearTime().getTime();
16871         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16872         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16873         var ddMatch = this.disabledDatesRE;
16874         var ddText = this.disabledDatesText;
16875         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16876         var ddaysText = this.disabledDaysText;
16877         var format = this.format;
16878         
16879         var setCellClass = function(cal, cell){
16880             cell.row = 0;
16881             cell.events = [];
16882             cell.more = [];
16883             //Roo.log('set Cell Class');
16884             cell.title = "";
16885             var t = d.getTime();
16886             
16887             //Roo.log(d);
16888             
16889             cell.dateValue = t;
16890             if(t == today){
16891                 cell.className += " fc-today";
16892                 cell.className += " fc-state-highlight";
16893                 cell.title = cal.todayText;
16894             }
16895             if(t == sel){
16896                 // disable highlight in other month..
16897                 //cell.className += " fc-state-highlight";
16898                 
16899             }
16900             // disabling
16901             if(t < min) {
16902                 cell.className = " fc-state-disabled";
16903                 cell.title = cal.minText;
16904                 return;
16905             }
16906             if(t > max) {
16907                 cell.className = " fc-state-disabled";
16908                 cell.title = cal.maxText;
16909                 return;
16910             }
16911             if(ddays){
16912                 if(ddays.indexOf(d.getDay()) != -1){
16913                     cell.title = ddaysText;
16914                     cell.className = " fc-state-disabled";
16915                 }
16916             }
16917             if(ddMatch && format){
16918                 var fvalue = d.dateFormat(format);
16919                 if(ddMatch.test(fvalue)){
16920                     cell.title = ddText.replace("%0", fvalue);
16921                     cell.className = " fc-state-disabled";
16922                 }
16923             }
16924             
16925             if (!cell.initialClassName) {
16926                 cell.initialClassName = cell.dom.className;
16927             }
16928             
16929             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16930         };
16931
16932         var i = 0;
16933         
16934         for(; i < startingPos; i++) {
16935             textEls[i].innerHTML = (++prevStart);
16936             d.setDate(d.getDate()+1);
16937             
16938             cells[i].className = "fc-past fc-other-month";
16939             setCellClass(this, cells[i]);
16940         }
16941         
16942         var intDay = 0;
16943         
16944         for(; i < days; i++){
16945             intDay = i - startingPos + 1;
16946             textEls[i].innerHTML = (intDay);
16947             d.setDate(d.getDate()+1);
16948             
16949             cells[i].className = ''; // "x-date-active";
16950             setCellClass(this, cells[i]);
16951         }
16952         var extraDays = 0;
16953         
16954         for(; i < 42; i++) {
16955             textEls[i].innerHTML = (++extraDays);
16956             d.setDate(d.getDate()+1);
16957             
16958             cells[i].className = "fc-future fc-other-month";
16959             setCellClass(this, cells[i]);
16960         }
16961         
16962         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16963         
16964         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16965         
16966         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16967         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16968         
16969         if(totalRows != 6){
16970             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16971             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16972         }
16973         
16974         this.fireEvent('monthchange', this, date);
16975         
16976         
16977         /*
16978         if(!this.internalRender){
16979             var main = this.el.dom.firstChild;
16980             var w = main.offsetWidth;
16981             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16982             Roo.fly(main).setWidth(w);
16983             this.internalRender = true;
16984             // opera does not respect the auto grow header center column
16985             // then, after it gets a width opera refuses to recalculate
16986             // without a second pass
16987             if(Roo.isOpera && !this.secondPass){
16988                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16989                 this.secondPass = true;
16990                 this.update.defer(10, this, [date]);
16991             }
16992         }
16993         */
16994         
16995     },
16996     
16997     findCell : function(dt) {
16998         dt = dt.clearTime().getTime();
16999         var ret = false;
17000         this.cells.each(function(c){
17001             //Roo.log("check " +c.dateValue + '?=' + dt);
17002             if(c.dateValue == dt){
17003                 ret = c;
17004                 return false;
17005             }
17006             return true;
17007         });
17008         
17009         return ret;
17010     },
17011     
17012     findCells : function(ev) {
17013         var s = ev.start.clone().clearTime().getTime();
17014        // Roo.log(s);
17015         var e= ev.end.clone().clearTime().getTime();
17016        // Roo.log(e);
17017         var ret = [];
17018         this.cells.each(function(c){
17019              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17020             
17021             if(c.dateValue > e){
17022                 return ;
17023             }
17024             if(c.dateValue < s){
17025                 return ;
17026             }
17027             ret.push(c);
17028         });
17029         
17030         return ret;    
17031     },
17032     
17033 //    findBestRow: function(cells)
17034 //    {
17035 //        var ret = 0;
17036 //        
17037 //        for (var i =0 ; i < cells.length;i++) {
17038 //            ret  = Math.max(cells[i].rows || 0,ret);
17039 //        }
17040 //        return ret;
17041 //        
17042 //    },
17043     
17044     
17045     addItem : function(ev)
17046     {
17047         // look for vertical location slot in
17048         var cells = this.findCells(ev);
17049         
17050 //        ev.row = this.findBestRow(cells);
17051         
17052         // work out the location.
17053         
17054         var crow = false;
17055         var rows = [];
17056         for(var i =0; i < cells.length; i++) {
17057             
17058             cells[i].row = cells[0].row;
17059             
17060             if(i == 0){
17061                 cells[i].row = cells[i].row + 1;
17062             }
17063             
17064             if (!crow) {
17065                 crow = {
17066                     start : cells[i],
17067                     end :  cells[i]
17068                 };
17069                 continue;
17070             }
17071             if (crow.start.getY() == cells[i].getY()) {
17072                 // on same row.
17073                 crow.end = cells[i];
17074                 continue;
17075             }
17076             // different row.
17077             rows.push(crow);
17078             crow = {
17079                 start: cells[i],
17080                 end : cells[i]
17081             };
17082             
17083         }
17084         
17085         rows.push(crow);
17086         ev.els = [];
17087         ev.rows = rows;
17088         ev.cells = cells;
17089         
17090         cells[0].events.push(ev);
17091         
17092         this.calevents.push(ev);
17093     },
17094     
17095     clearEvents: function() {
17096         
17097         if(!this.calevents){
17098             return;
17099         }
17100         
17101         Roo.each(this.cells.elements, function(c){
17102             c.row = 0;
17103             c.events = [];
17104             c.more = [];
17105         });
17106         
17107         Roo.each(this.calevents, function(e) {
17108             Roo.each(e.els, function(el) {
17109                 el.un('mouseenter' ,this.onEventEnter, this);
17110                 el.un('mouseleave' ,this.onEventLeave, this);
17111                 el.remove();
17112             },this);
17113         },this);
17114         
17115         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17116             e.remove();
17117         });
17118         
17119     },
17120     
17121     renderEvents: function()
17122     {   
17123         var _this = this;
17124         
17125         this.cells.each(function(c) {
17126             
17127             if(c.row < 5){
17128                 return;
17129             }
17130             
17131             var ev = c.events;
17132             
17133             var r = 4;
17134             if(c.row != c.events.length){
17135                 r = 4 - (4 - (c.row - c.events.length));
17136             }
17137             
17138             c.events = ev.slice(0, r);
17139             c.more = ev.slice(r);
17140             
17141             if(c.more.length && c.more.length == 1){
17142                 c.events.push(c.more.pop());
17143             }
17144             
17145             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17146             
17147         });
17148             
17149         this.cells.each(function(c) {
17150             
17151             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17152             
17153             
17154             for (var e = 0; e < c.events.length; e++){
17155                 var ev = c.events[e];
17156                 var rows = ev.rows;
17157                 
17158                 for(var i = 0; i < rows.length; i++) {
17159                 
17160                     // how many rows should it span..
17161
17162                     var  cfg = {
17163                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17164                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17165
17166                         unselectable : "on",
17167                         cn : [
17168                             {
17169                                 cls: 'fc-event-inner',
17170                                 cn : [
17171     //                                {
17172     //                                  tag:'span',
17173     //                                  cls: 'fc-event-time',
17174     //                                  html : cells.length > 1 ? '' : ev.time
17175     //                                },
17176                                     {
17177                                       tag:'span',
17178                                       cls: 'fc-event-title',
17179                                       html : String.format('{0}', ev.title)
17180                                     }
17181
17182
17183                                 ]
17184                             },
17185                             {
17186                                 cls: 'ui-resizable-handle ui-resizable-e',
17187                                 html : '&nbsp;&nbsp;&nbsp'
17188                             }
17189
17190                         ]
17191                     };
17192
17193                     if (i == 0) {
17194                         cfg.cls += ' fc-event-start';
17195                     }
17196                     if ((i+1) == rows.length) {
17197                         cfg.cls += ' fc-event-end';
17198                     }
17199
17200                     var ctr = _this.el.select('.fc-event-container',true).first();
17201                     var cg = ctr.createChild(cfg);
17202
17203                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17204                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17205
17206                     var r = (c.more.length) ? 1 : 0;
17207                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17208                     cg.setWidth(ebox.right - sbox.x -2);
17209
17210                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17211                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17212                     cg.on('click', _this.onEventClick, _this, ev);
17213
17214                     ev.els.push(cg);
17215                     
17216                 }
17217                 
17218             }
17219             
17220             
17221             if(c.more.length){
17222                 var  cfg = {
17223                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17224                     style : 'position: absolute',
17225                     unselectable : "on",
17226                     cn : [
17227                         {
17228                             cls: 'fc-event-inner',
17229                             cn : [
17230                                 {
17231                                   tag:'span',
17232                                   cls: 'fc-event-title',
17233                                   html : 'More'
17234                                 }
17235
17236
17237                             ]
17238                         },
17239                         {
17240                             cls: 'ui-resizable-handle ui-resizable-e',
17241                             html : '&nbsp;&nbsp;&nbsp'
17242                         }
17243
17244                     ]
17245                 };
17246
17247                 var ctr = _this.el.select('.fc-event-container',true).first();
17248                 var cg = ctr.createChild(cfg);
17249
17250                 var sbox = c.select('.fc-day-content',true).first().getBox();
17251                 var ebox = c.select('.fc-day-content',true).first().getBox();
17252                 //Roo.log(cg);
17253                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17254                 cg.setWidth(ebox.right - sbox.x -2);
17255
17256                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17257                 
17258             }
17259             
17260         });
17261         
17262         
17263         
17264     },
17265     
17266     onEventEnter: function (e, el,event,d) {
17267         this.fireEvent('evententer', this, el, event);
17268     },
17269     
17270     onEventLeave: function (e, el,event,d) {
17271         this.fireEvent('eventleave', this, el, event);
17272     },
17273     
17274     onEventClick: function (e, el,event,d) {
17275         this.fireEvent('eventclick', this, el, event);
17276     },
17277     
17278     onMonthChange: function () {
17279         this.store.load();
17280     },
17281     
17282     onMoreEventClick: function(e, el, more)
17283     {
17284         var _this = this;
17285         
17286         this.calpopover.placement = 'right';
17287         this.calpopover.setTitle('More');
17288         
17289         this.calpopover.setContent('');
17290         
17291         var ctr = this.calpopover.el.select('.popover-content', true).first();
17292         
17293         Roo.each(more, function(m){
17294             var cfg = {
17295                 cls : 'fc-event-hori fc-event-draggable',
17296                 html : m.title
17297             };
17298             var cg = ctr.createChild(cfg);
17299             
17300             cg.on('click', _this.onEventClick, _this, m);
17301         });
17302         
17303         this.calpopover.show(el);
17304         
17305         
17306     },
17307     
17308     onLoad: function () 
17309     {   
17310         this.calevents = [];
17311         var cal = this;
17312         
17313         if(this.store.getCount() > 0){
17314             this.store.data.each(function(d){
17315                cal.addItem({
17316                     id : d.data.id,
17317                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17318                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17319                     time : d.data.start_time,
17320                     title : d.data.title,
17321                     description : d.data.description,
17322                     venue : d.data.venue
17323                 });
17324             });
17325         }
17326         
17327         this.renderEvents();
17328         
17329         if(this.calevents.length && this.loadMask){
17330             this.maskEl.hide();
17331         }
17332     },
17333     
17334     onBeforeLoad: function()
17335     {
17336         this.clearEvents();
17337         if(this.loadMask){
17338             this.maskEl.show();
17339         }
17340     }
17341 });
17342
17343  
17344  /*
17345  * - LGPL
17346  *
17347  * element
17348  * 
17349  */
17350
17351 /**
17352  * @class Roo.bootstrap.Popover
17353  * @extends Roo.bootstrap.Component
17354  * Bootstrap Popover class
17355  * @cfg {String} html contents of the popover   (or false to use children..)
17356  * @cfg {String} title of popover (or false to hide)
17357  * @cfg {String} placement how it is placed
17358  * @cfg {String} trigger click || hover (or false to trigger manually)
17359  * @cfg {String} over what (parent or false to trigger manually.)
17360  * @cfg {Number} delay - delay before showing
17361  
17362  * @constructor
17363  * Create a new Popover
17364  * @param {Object} config The config object
17365  */
17366
17367 Roo.bootstrap.Popover = function(config){
17368     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17369     
17370     this.addEvents({
17371         // raw events
17372          /**
17373          * @event show
17374          * After the popover show
17375          * 
17376          * @param {Roo.bootstrap.Popover} this
17377          */
17378         "show" : true,
17379         /**
17380          * @event hide
17381          * After the popover hide
17382          * 
17383          * @param {Roo.bootstrap.Popover} this
17384          */
17385         "hide" : true
17386     });
17387 };
17388
17389 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17390     
17391     title: 'Fill in a title',
17392     html: false,
17393     
17394     placement : 'right',
17395     trigger : 'hover', // hover
17396     
17397     delay : 0,
17398     
17399     over: 'parent',
17400     
17401     can_build_overlaid : false,
17402     
17403     getChildContainer : function()
17404     {
17405         return this.el.select('.popover-content',true).first();
17406     },
17407     
17408     getAutoCreate : function(){
17409          
17410         var cfg = {
17411            cls : 'popover roo-dynamic',
17412            style: 'display:block',
17413            cn : [
17414                 {
17415                     cls : 'arrow'
17416                 },
17417                 {
17418                     cls : 'popover-inner',
17419                     cn : [
17420                         {
17421                             tag: 'h3',
17422                             cls: 'popover-title',
17423                             html : this.title
17424                         },
17425                         {
17426                             cls : 'popover-content',
17427                             html : this.html
17428                         }
17429                     ]
17430                     
17431                 }
17432            ]
17433         };
17434         
17435         return cfg;
17436     },
17437     setTitle: function(str)
17438     {
17439         this.title = str;
17440         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17441     },
17442     setContent: function(str)
17443     {
17444         this.html = str;
17445         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17446     },
17447     // as it get's added to the bottom of the page.
17448     onRender : function(ct, position)
17449     {
17450         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17451         if(!this.el){
17452             var cfg = Roo.apply({},  this.getAutoCreate());
17453             cfg.id = Roo.id();
17454             
17455             if (this.cls) {
17456                 cfg.cls += ' ' + this.cls;
17457             }
17458             if (this.style) {
17459                 cfg.style = this.style;
17460             }
17461             //Roo.log("adding to ");
17462             this.el = Roo.get(document.body).createChild(cfg, position);
17463 //            Roo.log(this.el);
17464         }
17465         this.initEvents();
17466     },
17467     
17468     initEvents : function()
17469     {
17470         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17471         this.el.enableDisplayMode('block');
17472         this.el.hide();
17473         if (this.over === false) {
17474             return; 
17475         }
17476         if (this.triggers === false) {
17477             return;
17478         }
17479         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17480         var triggers = this.trigger ? this.trigger.split(' ') : [];
17481         Roo.each(triggers, function(trigger) {
17482         
17483             if (trigger == 'click') {
17484                 on_el.on('click', this.toggle, this);
17485             } else if (trigger != 'manual') {
17486                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17487                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17488       
17489                 on_el.on(eventIn  ,this.enter, this);
17490                 on_el.on(eventOut, this.leave, this);
17491             }
17492         }, this);
17493         
17494     },
17495     
17496     
17497     // private
17498     timeout : null,
17499     hoverState : null,
17500     
17501     toggle : function () {
17502         this.hoverState == 'in' ? this.leave() : this.enter();
17503     },
17504     
17505     enter : function () {
17506         
17507         clearTimeout(this.timeout);
17508     
17509         this.hoverState = 'in';
17510     
17511         if (!this.delay || !this.delay.show) {
17512             this.show();
17513             return;
17514         }
17515         var _t = this;
17516         this.timeout = setTimeout(function () {
17517             if (_t.hoverState == 'in') {
17518                 _t.show();
17519             }
17520         }, this.delay.show)
17521     },
17522     
17523     leave : function() {
17524         clearTimeout(this.timeout);
17525     
17526         this.hoverState = 'out';
17527     
17528         if (!this.delay || !this.delay.hide) {
17529             this.hide();
17530             return;
17531         }
17532         var _t = this;
17533         this.timeout = setTimeout(function () {
17534             if (_t.hoverState == 'out') {
17535                 _t.hide();
17536             }
17537         }, this.delay.hide)
17538     },
17539     
17540     show : function (on_el)
17541     {
17542         if (!on_el) {
17543             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17544         }
17545         
17546         // set content.
17547         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17548         if (this.html !== false) {
17549             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17550         }
17551         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17552         if (!this.title.length) {
17553             this.el.select('.popover-title',true).hide();
17554         }
17555         
17556         var placement = typeof this.placement == 'function' ?
17557             this.placement.call(this, this.el, on_el) :
17558             this.placement;
17559             
17560         var autoToken = /\s?auto?\s?/i;
17561         var autoPlace = autoToken.test(placement);
17562         if (autoPlace) {
17563             placement = placement.replace(autoToken, '') || 'top';
17564         }
17565         
17566         //this.el.detach()
17567         //this.el.setXY([0,0]);
17568         this.el.show();
17569         this.el.dom.style.display='block';
17570         this.el.addClass(placement);
17571         
17572         //this.el.appendTo(on_el);
17573         
17574         var p = this.getPosition();
17575         var box = this.el.getBox();
17576         
17577         if (autoPlace) {
17578             // fixme..
17579         }
17580         var align = Roo.bootstrap.Popover.alignment[placement];
17581         
17582 //        Roo.log(align);
17583         this.el.alignTo(on_el, align[0],align[1]);
17584         //var arrow = this.el.select('.arrow',true).first();
17585         //arrow.set(align[2], 
17586         
17587         this.el.addClass('in');
17588         
17589         
17590         if (this.el.hasClass('fade')) {
17591             // fade it?
17592         }
17593         
17594         this.hoverState = 'in';
17595         
17596         this.fireEvent('show', this);
17597         
17598     },
17599     hide : function()
17600     {
17601         this.el.setXY([0,0]);
17602         this.el.removeClass('in');
17603         this.el.hide();
17604         this.hoverState = null;
17605         
17606         this.fireEvent('hide', this);
17607     }
17608     
17609 });
17610
17611 Roo.bootstrap.Popover.alignment = {
17612     'left' : ['r-l', [-10,0], 'right'],
17613     'right' : ['l-r', [10,0], 'left'],
17614     'bottom' : ['t-b', [0,10], 'top'],
17615     'top' : [ 'b-t', [0,-10], 'bottom']
17616 };
17617
17618  /*
17619  * - LGPL
17620  *
17621  * Progress
17622  * 
17623  */
17624
17625 /**
17626  * @class Roo.bootstrap.Progress
17627  * @extends Roo.bootstrap.Component
17628  * Bootstrap Progress class
17629  * @cfg {Boolean} striped striped of the progress bar
17630  * @cfg {Boolean} active animated of the progress bar
17631  * 
17632  * 
17633  * @constructor
17634  * Create a new Progress
17635  * @param {Object} config The config object
17636  */
17637
17638 Roo.bootstrap.Progress = function(config){
17639     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17640 };
17641
17642 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17643     
17644     striped : false,
17645     active: false,
17646     
17647     getAutoCreate : function(){
17648         var cfg = {
17649             tag: 'div',
17650             cls: 'progress'
17651         };
17652         
17653         
17654         if(this.striped){
17655             cfg.cls += ' progress-striped';
17656         }
17657       
17658         if(this.active){
17659             cfg.cls += ' active';
17660         }
17661         
17662         
17663         return cfg;
17664     }
17665    
17666 });
17667
17668  
17669
17670  /*
17671  * - LGPL
17672  *
17673  * ProgressBar
17674  * 
17675  */
17676
17677 /**
17678  * @class Roo.bootstrap.ProgressBar
17679  * @extends Roo.bootstrap.Component
17680  * Bootstrap ProgressBar class
17681  * @cfg {Number} aria_valuenow aria-value now
17682  * @cfg {Number} aria_valuemin aria-value min
17683  * @cfg {Number} aria_valuemax aria-value max
17684  * @cfg {String} label label for the progress bar
17685  * @cfg {String} panel (success | info | warning | danger )
17686  * @cfg {String} role role of the progress bar
17687  * @cfg {String} sr_only text
17688  * 
17689  * 
17690  * @constructor
17691  * Create a new ProgressBar
17692  * @param {Object} config The config object
17693  */
17694
17695 Roo.bootstrap.ProgressBar = function(config){
17696     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17697 };
17698
17699 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17700     
17701     aria_valuenow : 0,
17702     aria_valuemin : 0,
17703     aria_valuemax : 100,
17704     label : false,
17705     panel : false,
17706     role : false,
17707     sr_only: false,
17708     
17709     getAutoCreate : function()
17710     {
17711         
17712         var cfg = {
17713             tag: 'div',
17714             cls: 'progress-bar',
17715             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17716         };
17717         
17718         if(this.sr_only){
17719             cfg.cn = {
17720                 tag: 'span',
17721                 cls: 'sr-only',
17722                 html: this.sr_only
17723             }
17724         }
17725         
17726         if(this.role){
17727             cfg.role = this.role;
17728         }
17729         
17730         if(this.aria_valuenow){
17731             cfg['aria-valuenow'] = this.aria_valuenow;
17732         }
17733         
17734         if(this.aria_valuemin){
17735             cfg['aria-valuemin'] = this.aria_valuemin;
17736         }
17737         
17738         if(this.aria_valuemax){
17739             cfg['aria-valuemax'] = this.aria_valuemax;
17740         }
17741         
17742         if(this.label && !this.sr_only){
17743             cfg.html = this.label;
17744         }
17745         
17746         if(this.panel){
17747             cfg.cls += ' progress-bar-' + this.panel;
17748         }
17749         
17750         return cfg;
17751     },
17752     
17753     update : function(aria_valuenow)
17754     {
17755         this.aria_valuenow = aria_valuenow;
17756         
17757         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17758     }
17759    
17760 });
17761
17762  
17763
17764  /*
17765  * - LGPL
17766  *
17767  * column
17768  * 
17769  */
17770
17771 /**
17772  * @class Roo.bootstrap.TabGroup
17773  * @extends Roo.bootstrap.Column
17774  * Bootstrap Column class
17775  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17776  * @cfg {Boolean} carousel true to make the group behave like a carousel
17777  * @cfg {Boolean} bullets show bullets for the panels
17778  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17779  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17780  * @cfg {Boolean} showarrow (true|false) show arrow default true
17781  * 
17782  * @constructor
17783  * Create a new TabGroup
17784  * @param {Object} config The config object
17785  */
17786
17787 Roo.bootstrap.TabGroup = function(config){
17788     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17789     if (!this.navId) {
17790         this.navId = Roo.id();
17791     }
17792     this.tabs = [];
17793     Roo.bootstrap.TabGroup.register(this);
17794     
17795 };
17796
17797 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17798     
17799     carousel : false,
17800     transition : false,
17801     bullets : 0,
17802     timer : 0,
17803     autoslide : false,
17804     slideFn : false,
17805     slideOnTouch : false,
17806     showarrow : true,
17807     
17808     getAutoCreate : function()
17809     {
17810         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17811         
17812         cfg.cls += ' tab-content';
17813         
17814         if (this.carousel) {
17815             cfg.cls += ' carousel slide';
17816             
17817             cfg.cn = [{
17818                cls : 'carousel-inner',
17819                cn : []
17820             }];
17821         
17822             if(this.bullets  && !Roo.isTouch){
17823                 
17824                 var bullets = {
17825                     cls : 'carousel-bullets',
17826                     cn : []
17827                 };
17828                
17829                 if(this.bullets_cls){
17830                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17831                 }
17832                 
17833                 bullets.cn.push({
17834                     cls : 'clear'
17835                 });
17836                 
17837                 cfg.cn[0].cn.push(bullets);
17838             }
17839             
17840             if(this.showarrow){
17841                 cfg.cn[0].cn.push({
17842                     tag : 'div',
17843                     class : 'carousel-arrow',
17844                     cn : [
17845                         {
17846                             tag : 'div',
17847                             class : 'carousel-prev',
17848                             cn : [
17849                                 {
17850                                     tag : 'i',
17851                                     class : 'fa fa-chevron-left'
17852                                 }
17853                             ]
17854                         },
17855                         {
17856                             tag : 'div',
17857                             class : 'carousel-next',
17858                             cn : [
17859                                 {
17860                                     tag : 'i',
17861                                     class : 'fa fa-chevron-right'
17862                                 }
17863                             ]
17864                         }
17865                     ]
17866                 });
17867             }
17868             
17869         }
17870         
17871         return cfg;
17872     },
17873     
17874     initEvents:  function()
17875     {
17876 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17877 //            this.el.on("touchstart", this.onTouchStart, this);
17878 //        }
17879         
17880         if(this.autoslide){
17881             var _this = this;
17882             
17883             this.slideFn = window.setInterval(function() {
17884                 _this.showPanelNext();
17885             }, this.timer);
17886         }
17887         
17888         if(this.showarrow){
17889             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17890             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17891         }
17892         
17893         
17894     },
17895     
17896 //    onTouchStart : function(e, el, o)
17897 //    {
17898 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17899 //            return;
17900 //        }
17901 //        
17902 //        this.showPanelNext();
17903 //    },
17904     
17905     
17906     getChildContainer : function()
17907     {
17908         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17909     },
17910     
17911     /**
17912     * register a Navigation item
17913     * @param {Roo.bootstrap.NavItem} the navitem to add
17914     */
17915     register : function(item)
17916     {
17917         this.tabs.push( item);
17918         item.navId = this.navId; // not really needed..
17919         this.addBullet();
17920     
17921     },
17922     
17923     getActivePanel : function()
17924     {
17925         var r = false;
17926         Roo.each(this.tabs, function(t) {
17927             if (t.active) {
17928                 r = t;
17929                 return false;
17930             }
17931             return null;
17932         });
17933         return r;
17934         
17935     },
17936     getPanelByName : function(n)
17937     {
17938         var r = false;
17939         Roo.each(this.tabs, function(t) {
17940             if (t.tabId == n) {
17941                 r = t;
17942                 return false;
17943             }
17944             return null;
17945         });
17946         return r;
17947     },
17948     indexOfPanel : function(p)
17949     {
17950         var r = false;
17951         Roo.each(this.tabs, function(t,i) {
17952             if (t.tabId == p.tabId) {
17953                 r = i;
17954                 return false;
17955             }
17956             return null;
17957         });
17958         return r;
17959     },
17960     /**
17961      * show a specific panel
17962      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17963      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17964      */
17965     showPanel : function (pan)
17966     {
17967         if(this.transition || typeof(pan) == 'undefined'){
17968             Roo.log("waiting for the transitionend");
17969             return;
17970         }
17971         
17972         if (typeof(pan) == 'number') {
17973             pan = this.tabs[pan];
17974         }
17975         
17976         if (typeof(pan) == 'string') {
17977             pan = this.getPanelByName(pan);
17978         }
17979         
17980         var cur = this.getActivePanel();
17981         
17982         if(!pan || !cur){
17983             Roo.log('pan or acitve pan is undefined');
17984             return false;
17985         }
17986         
17987         if (pan.tabId == this.getActivePanel().tabId) {
17988             return true;
17989         }
17990         
17991         if (false === cur.fireEvent('beforedeactivate')) {
17992             return false;
17993         }
17994         
17995         if(this.bullets > 0 && !Roo.isTouch){
17996             this.setActiveBullet(this.indexOfPanel(pan));
17997         }
17998         
17999         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18000             
18001             this.transition = true;
18002             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18003             var lr = dir == 'next' ? 'left' : 'right';
18004             pan.el.addClass(dir); // or prev
18005             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18006             cur.el.addClass(lr); // or right
18007             pan.el.addClass(lr);
18008             
18009             var _this = this;
18010             cur.el.on('transitionend', function() {
18011                 Roo.log("trans end?");
18012                 
18013                 pan.el.removeClass([lr,dir]);
18014                 pan.setActive(true);
18015                 
18016                 cur.el.removeClass([lr]);
18017                 cur.setActive(false);
18018                 
18019                 _this.transition = false;
18020                 
18021             }, this, { single:  true } );
18022             
18023             return true;
18024         }
18025         
18026         cur.setActive(false);
18027         pan.setActive(true);
18028         
18029         return true;
18030         
18031     },
18032     showPanelNext : function()
18033     {
18034         var i = this.indexOfPanel(this.getActivePanel());
18035         
18036         if (i >= this.tabs.length - 1 && !this.autoslide) {
18037             return;
18038         }
18039         
18040         if (i >= this.tabs.length - 1 && this.autoslide) {
18041             i = -1;
18042         }
18043         
18044         this.showPanel(this.tabs[i+1]);
18045     },
18046     
18047     showPanelPrev : function()
18048     {
18049         var i = this.indexOfPanel(this.getActivePanel());
18050         
18051         if (i  < 1 && !this.autoslide) {
18052             return;
18053         }
18054         
18055         if (i < 1 && this.autoslide) {
18056             i = this.tabs.length;
18057         }
18058         
18059         this.showPanel(this.tabs[i-1]);
18060     },
18061     
18062     
18063     addBullet: function()
18064     {
18065         if(!this.bullets || Roo.isTouch){
18066             return;
18067         }
18068         var ctr = this.el.select('.carousel-bullets',true).first();
18069         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18070         var bullet = ctr.createChild({
18071             cls : 'bullet bullet-' + i
18072         },ctr.dom.lastChild);
18073         
18074         
18075         var _this = this;
18076         
18077         bullet.on('click', (function(e, el, o, ii, t){
18078
18079             e.preventDefault();
18080
18081             this.showPanel(ii);
18082
18083             if(this.autoslide && this.slideFn){
18084                 clearInterval(this.slideFn);
18085                 this.slideFn = window.setInterval(function() {
18086                     _this.showPanelNext();
18087                 }, this.timer);
18088             }
18089
18090         }).createDelegate(this, [i, bullet], true));
18091                 
18092         
18093     },
18094      
18095     setActiveBullet : function(i)
18096     {
18097         if(Roo.isTouch){
18098             return;
18099         }
18100         
18101         Roo.each(this.el.select('.bullet', true).elements, function(el){
18102             el.removeClass('selected');
18103         });
18104
18105         var bullet = this.el.select('.bullet-' + i, true).first();
18106         
18107         if(!bullet){
18108             return;
18109         }
18110         
18111         bullet.addClass('selected');
18112     }
18113     
18114     
18115   
18116 });
18117
18118  
18119
18120  
18121  
18122 Roo.apply(Roo.bootstrap.TabGroup, {
18123     
18124     groups: {},
18125      /**
18126     * register a Navigation Group
18127     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18128     */
18129     register : function(navgrp)
18130     {
18131         this.groups[navgrp.navId] = navgrp;
18132         
18133     },
18134     /**
18135     * fetch a Navigation Group based on the navigation ID
18136     * if one does not exist , it will get created.
18137     * @param {string} the navgroup to add
18138     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18139     */
18140     get: function(navId) {
18141         if (typeof(this.groups[navId]) == 'undefined') {
18142             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18143         }
18144         return this.groups[navId] ;
18145     }
18146     
18147     
18148     
18149 });
18150
18151  /*
18152  * - LGPL
18153  *
18154  * TabPanel
18155  * 
18156  */
18157
18158 /**
18159  * @class Roo.bootstrap.TabPanel
18160  * @extends Roo.bootstrap.Component
18161  * Bootstrap TabPanel class
18162  * @cfg {Boolean} active panel active
18163  * @cfg {String} html panel content
18164  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18165  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18166  * @cfg {String} href click to link..
18167  * 
18168  * 
18169  * @constructor
18170  * Create a new TabPanel
18171  * @param {Object} config The config object
18172  */
18173
18174 Roo.bootstrap.TabPanel = function(config){
18175     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18176     this.addEvents({
18177         /**
18178              * @event changed
18179              * Fires when the active status changes
18180              * @param {Roo.bootstrap.TabPanel} this
18181              * @param {Boolean} state the new state
18182             
18183          */
18184         'changed': true,
18185         /**
18186              * @event beforedeactivate
18187              * Fires before a tab is de-activated - can be used to do validation on a form.
18188              * @param {Roo.bootstrap.TabPanel} this
18189              * @return {Boolean} false if there is an error
18190             
18191          */
18192         'beforedeactivate': true
18193      });
18194     
18195     this.tabId = this.tabId || Roo.id();
18196   
18197 };
18198
18199 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18200     
18201     active: false,
18202     html: false,
18203     tabId: false,
18204     navId : false,
18205     href : '',
18206     
18207     getAutoCreate : function(){
18208         var cfg = {
18209             tag: 'div',
18210             // item is needed for carousel - not sure if it has any effect otherwise
18211             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18212             html: this.html || ''
18213         };
18214         
18215         if(this.active){
18216             cfg.cls += ' active';
18217         }
18218         
18219         if(this.tabId){
18220             cfg.tabId = this.tabId;
18221         }
18222         
18223         
18224         return cfg;
18225     },
18226     
18227     initEvents:  function()
18228     {
18229         var p = this.parent();
18230         
18231         this.navId = this.navId || p.navId;
18232         
18233         if (typeof(this.navId) != 'undefined') {
18234             // not really needed.. but just in case.. parent should be a NavGroup.
18235             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18236             
18237             tg.register(this);
18238             
18239             var i = tg.tabs.length - 1;
18240             
18241             if(this.active && tg.bullets > 0 && i < tg.bullets){
18242                 tg.setActiveBullet(i);
18243             }
18244         }
18245         
18246         this.el.on('click', this.onClick, this);
18247         
18248         if(Roo.isTouch){
18249             this.el.on("touchstart", this.onTouchStart, this);
18250             this.el.on("touchmove", this.onTouchMove, this);
18251             this.el.on("touchend", this.onTouchEnd, this);
18252         }
18253         
18254     },
18255     
18256     onRender : function(ct, position)
18257     {
18258         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18259     },
18260     
18261     setActive : function(state)
18262     {
18263         Roo.log("panel - set active " + this.tabId + "=" + state);
18264         
18265         this.active = state;
18266         if (!state) {
18267             this.el.removeClass('active');
18268             
18269         } else  if (!this.el.hasClass('active')) {
18270             this.el.addClass('active');
18271         }
18272         
18273         this.fireEvent('changed', this, state);
18274     },
18275     
18276     onClick : function(e)
18277     {
18278         e.preventDefault();
18279         
18280         if(!this.href.length){
18281             return;
18282         }
18283         
18284         window.location.href = this.href;
18285     },
18286     
18287     startX : 0,
18288     startY : 0,
18289     endX : 0,
18290     endY : 0,
18291     swiping : false,
18292     
18293     onTouchStart : function(e)
18294     {
18295         this.swiping = false;
18296         
18297         this.startX = e.browserEvent.touches[0].clientX;
18298         this.startY = e.browserEvent.touches[0].clientY;
18299     },
18300     
18301     onTouchMove : function(e)
18302     {
18303         this.swiping = true;
18304         
18305         this.endX = e.browserEvent.touches[0].clientX;
18306         this.endY = e.browserEvent.touches[0].clientY;
18307     },
18308     
18309     onTouchEnd : function(e)
18310     {
18311         if(!this.swiping){
18312             this.onClick(e);
18313             return;
18314         }
18315         
18316         var tabGroup = this.parent();
18317         
18318         if(this.endX > this.startX){ // swiping right
18319             tabGroup.showPanelPrev();
18320             return;
18321         }
18322         
18323         if(this.startX > this.endX){ // swiping left
18324             tabGroup.showPanelNext();
18325             return;
18326         }
18327     }
18328     
18329     
18330 });
18331  
18332
18333  
18334
18335  /*
18336  * - LGPL
18337  *
18338  * DateField
18339  * 
18340  */
18341
18342 /**
18343  * @class Roo.bootstrap.DateField
18344  * @extends Roo.bootstrap.Input
18345  * Bootstrap DateField class
18346  * @cfg {Number} weekStart default 0
18347  * @cfg {String} viewMode default empty, (months|years)
18348  * @cfg {String} minViewMode default empty, (months|years)
18349  * @cfg {Number} startDate default -Infinity
18350  * @cfg {Number} endDate default Infinity
18351  * @cfg {Boolean} todayHighlight default false
18352  * @cfg {Boolean} todayBtn default false
18353  * @cfg {Boolean} calendarWeeks default false
18354  * @cfg {Object} daysOfWeekDisabled default empty
18355  * @cfg {Boolean} singleMode default false (true | false)
18356  * 
18357  * @cfg {Boolean} keyboardNavigation default true
18358  * @cfg {String} language default en
18359  * 
18360  * @constructor
18361  * Create a new DateField
18362  * @param {Object} config The config object
18363  */
18364
18365 Roo.bootstrap.DateField = function(config){
18366     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18367      this.addEvents({
18368             /**
18369              * @event show
18370              * Fires when this field show.
18371              * @param {Roo.bootstrap.DateField} this
18372              * @param {Mixed} date The date value
18373              */
18374             show : true,
18375             /**
18376              * @event show
18377              * Fires when this field hide.
18378              * @param {Roo.bootstrap.DateField} this
18379              * @param {Mixed} date The date value
18380              */
18381             hide : true,
18382             /**
18383              * @event select
18384              * Fires when select a date.
18385              * @param {Roo.bootstrap.DateField} this
18386              * @param {Mixed} date The date value
18387              */
18388             select : true,
18389             /**
18390              * @event beforeselect
18391              * Fires when before select a date.
18392              * @param {Roo.bootstrap.DateField} this
18393              * @param {Mixed} date The date value
18394              */
18395             beforeselect : true
18396         });
18397 };
18398
18399 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18400     
18401     /**
18402      * @cfg {String} format
18403      * The default date format string which can be overriden for localization support.  The format must be
18404      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18405      */
18406     format : "m/d/y",
18407     /**
18408      * @cfg {String} altFormats
18409      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18410      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18411      */
18412     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18413     
18414     weekStart : 0,
18415     
18416     viewMode : '',
18417     
18418     minViewMode : '',
18419     
18420     todayHighlight : false,
18421     
18422     todayBtn: false,
18423     
18424     language: 'en',
18425     
18426     keyboardNavigation: true,
18427     
18428     calendarWeeks: false,
18429     
18430     startDate: -Infinity,
18431     
18432     endDate: Infinity,
18433     
18434     daysOfWeekDisabled: [],
18435     
18436     _events: [],
18437     
18438     singleMode : false,
18439     
18440     UTCDate: function()
18441     {
18442         return new Date(Date.UTC.apply(Date, arguments));
18443     },
18444     
18445     UTCToday: function()
18446     {
18447         var today = new Date();
18448         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18449     },
18450     
18451     getDate: function() {
18452             var d = this.getUTCDate();
18453             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18454     },
18455     
18456     getUTCDate: function() {
18457             return this.date;
18458     },
18459     
18460     setDate: function(d) {
18461             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18462     },
18463     
18464     setUTCDate: function(d) {
18465             this.date = d;
18466             this.setValue(this.formatDate(this.date));
18467     },
18468         
18469     onRender: function(ct, position)
18470     {
18471         
18472         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18473         
18474         this.language = this.language || 'en';
18475         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18476         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18477         
18478         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18479         this.format = this.format || 'm/d/y';
18480         this.isInline = false;
18481         this.isInput = true;
18482         this.component = this.el.select('.add-on', true).first() || false;
18483         this.component = (this.component && this.component.length === 0) ? false : this.component;
18484         this.hasInput = this.component && this.inputEl().length;
18485         
18486         if (typeof(this.minViewMode === 'string')) {
18487             switch (this.minViewMode) {
18488                 case 'months':
18489                     this.minViewMode = 1;
18490                     break;
18491                 case 'years':
18492                     this.minViewMode = 2;
18493                     break;
18494                 default:
18495                     this.minViewMode = 0;
18496                     break;
18497             }
18498         }
18499         
18500         if (typeof(this.viewMode === 'string')) {
18501             switch (this.viewMode) {
18502                 case 'months':
18503                     this.viewMode = 1;
18504                     break;
18505                 case 'years':
18506                     this.viewMode = 2;
18507                     break;
18508                 default:
18509                     this.viewMode = 0;
18510                     break;
18511             }
18512         }
18513                 
18514         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18515         
18516 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18517         
18518         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18519         
18520         this.picker().on('mousedown', this.onMousedown, this);
18521         this.picker().on('click', this.onClick, this);
18522         
18523         this.picker().addClass('datepicker-dropdown');
18524         
18525         this.startViewMode = this.viewMode;
18526         
18527         if(this.singleMode){
18528             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18529                 v.setVisibilityMode(Roo.Element.DISPLAY);
18530                 v.hide();
18531             });
18532             
18533             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18534                 v.setStyle('width', '189px');
18535             });
18536         }
18537         
18538         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18539             if(!this.calendarWeeks){
18540                 v.remove();
18541                 return;
18542             }
18543             
18544             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18545             v.attr('colspan', function(i, val){
18546                 return parseInt(val) + 1;
18547             });
18548         });
18549                         
18550         
18551         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18552         
18553         this.setStartDate(this.startDate);
18554         this.setEndDate(this.endDate);
18555         
18556         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18557         
18558         this.fillDow();
18559         this.fillMonths();
18560         this.update();
18561         this.showMode();
18562         
18563         if(this.isInline) {
18564             this.show();
18565         }
18566     },
18567     
18568     picker : function()
18569     {
18570         return this.pickerEl;
18571 //        return this.el.select('.datepicker', true).first();
18572     },
18573     
18574     fillDow: function()
18575     {
18576         var dowCnt = this.weekStart;
18577         
18578         var dow = {
18579             tag: 'tr',
18580             cn: [
18581                 
18582             ]
18583         };
18584         
18585         if(this.calendarWeeks){
18586             dow.cn.push({
18587                 tag: 'th',
18588                 cls: 'cw',
18589                 html: '&nbsp;'
18590             })
18591         }
18592         
18593         while (dowCnt < this.weekStart + 7) {
18594             dow.cn.push({
18595                 tag: 'th',
18596                 cls: 'dow',
18597                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18598             });
18599         }
18600         
18601         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18602     },
18603     
18604     fillMonths: function()
18605     {    
18606         var i = 0;
18607         var months = this.picker().select('>.datepicker-months td', true).first();
18608         
18609         months.dom.innerHTML = '';
18610         
18611         while (i < 12) {
18612             var month = {
18613                 tag: 'span',
18614                 cls: 'month',
18615                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18616             };
18617             
18618             months.createChild(month);
18619         }
18620         
18621     },
18622     
18623     update: function()
18624     {
18625         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;
18626         
18627         if (this.date < this.startDate) {
18628             this.viewDate = new Date(this.startDate);
18629         } else if (this.date > this.endDate) {
18630             this.viewDate = new Date(this.endDate);
18631         } else {
18632             this.viewDate = new Date(this.date);
18633         }
18634         
18635         this.fill();
18636     },
18637     
18638     fill: function() 
18639     {
18640         var d = new Date(this.viewDate),
18641                 year = d.getUTCFullYear(),
18642                 month = d.getUTCMonth(),
18643                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18644                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18645                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18646                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18647                 currentDate = this.date && this.date.valueOf(),
18648                 today = this.UTCToday();
18649         
18650         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18651         
18652 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18653         
18654 //        this.picker.select('>tfoot th.today').
18655 //                                              .text(dates[this.language].today)
18656 //                                              .toggle(this.todayBtn !== false);
18657     
18658         this.updateNavArrows();
18659         this.fillMonths();
18660                                                 
18661         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18662         
18663         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18664          
18665         prevMonth.setUTCDate(day);
18666         
18667         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18668         
18669         var nextMonth = new Date(prevMonth);
18670         
18671         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18672         
18673         nextMonth = nextMonth.valueOf();
18674         
18675         var fillMonths = false;
18676         
18677         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18678         
18679         while(prevMonth.valueOf() <= nextMonth) {
18680             var clsName = '';
18681             
18682             if (prevMonth.getUTCDay() === this.weekStart) {
18683                 if(fillMonths){
18684                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18685                 }
18686                     
18687                 fillMonths = {
18688                     tag: 'tr',
18689                     cn: []
18690                 };
18691                 
18692                 if(this.calendarWeeks){
18693                     // ISO 8601: First week contains first thursday.
18694                     // ISO also states week starts on Monday, but we can be more abstract here.
18695                     var
18696                     // Start of current week: based on weekstart/current date
18697                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18698                     // Thursday of this week
18699                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18700                     // First Thursday of year, year from thursday
18701                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18702                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18703                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18704                     
18705                     fillMonths.cn.push({
18706                         tag: 'td',
18707                         cls: 'cw',
18708                         html: calWeek
18709                     });
18710                 }
18711             }
18712             
18713             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18714                 clsName += ' old';
18715             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18716                 clsName += ' new';
18717             }
18718             if (this.todayHighlight &&
18719                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18720                 prevMonth.getUTCMonth() == today.getMonth() &&
18721                 prevMonth.getUTCDate() == today.getDate()) {
18722                 clsName += ' today';
18723             }
18724             
18725             if (currentDate && prevMonth.valueOf() === currentDate) {
18726                 clsName += ' active';
18727             }
18728             
18729             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18730                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18731                     clsName += ' disabled';
18732             }
18733             
18734             fillMonths.cn.push({
18735                 tag: 'td',
18736                 cls: 'day ' + clsName,
18737                 html: prevMonth.getDate()
18738             });
18739             
18740             prevMonth.setDate(prevMonth.getDate()+1);
18741         }
18742           
18743         var currentYear = this.date && this.date.getUTCFullYear();
18744         var currentMonth = this.date && this.date.getUTCMonth();
18745         
18746         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18747         
18748         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18749             v.removeClass('active');
18750             
18751             if(currentYear === year && k === currentMonth){
18752                 v.addClass('active');
18753             }
18754             
18755             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18756                 v.addClass('disabled');
18757             }
18758             
18759         });
18760         
18761         
18762         year = parseInt(year/10, 10) * 10;
18763         
18764         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18765         
18766         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18767         
18768         year -= 1;
18769         for (var i = -1; i < 11; i++) {
18770             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18771                 tag: 'span',
18772                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18773                 html: year
18774             });
18775             
18776             year += 1;
18777         }
18778     },
18779     
18780     showMode: function(dir) 
18781     {
18782         if (dir) {
18783             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18784         }
18785         
18786         Roo.each(this.picker().select('>div',true).elements, function(v){
18787             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18788             v.hide();
18789         });
18790         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18791     },
18792     
18793     place: function()
18794     {
18795         if(this.isInline) {
18796             return;
18797         }
18798         
18799         this.picker().removeClass(['bottom', 'top']);
18800         
18801         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18802             /*
18803              * place to the top of element!
18804              *
18805              */
18806             
18807             this.picker().addClass('top');
18808             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18809             
18810             return;
18811         }
18812         
18813         this.picker().addClass('bottom');
18814         
18815         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18816     },
18817     
18818     parseDate : function(value)
18819     {
18820         if(!value || value instanceof Date){
18821             return value;
18822         }
18823         var v = Date.parseDate(value, this.format);
18824         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18825             v = Date.parseDate(value, 'Y-m-d');
18826         }
18827         if(!v && this.altFormats){
18828             if(!this.altFormatsArray){
18829                 this.altFormatsArray = this.altFormats.split("|");
18830             }
18831             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18832                 v = Date.parseDate(value, this.altFormatsArray[i]);
18833             }
18834         }
18835         return v;
18836     },
18837     
18838     formatDate : function(date, fmt)
18839     {   
18840         return (!date || !(date instanceof Date)) ?
18841         date : date.dateFormat(fmt || this.format);
18842     },
18843     
18844     onFocus : function()
18845     {
18846         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18847         this.show();
18848     },
18849     
18850     onBlur : function()
18851     {
18852         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18853         
18854         var d = this.inputEl().getValue();
18855         
18856         this.setValue(d);
18857                 
18858         this.hide();
18859     },
18860     
18861     showPopup : function()
18862     {
18863         this.picker().show();
18864         this.update();
18865         this.place();
18866         
18867         this.fireEvent('showpopup', this, this.date);
18868     },
18869     
18870     hidePopup : function()
18871     {
18872         if(this.isInline) {
18873             return;
18874         }
18875         this.picker().hide();
18876         this.viewMode = this.startViewMode;
18877         this.showMode();
18878         
18879         this.fireEvent('hidepopup', this, this.date);
18880         
18881     },
18882     
18883     onMousedown: function(e)
18884     {
18885         e.stopPropagation();
18886         e.preventDefault();
18887     },
18888     
18889     keyup: function(e)
18890     {
18891         Roo.bootstrap.DateField.superclass.keyup.call(this);
18892         this.update();
18893     },
18894
18895     setValue: function(v)
18896     {
18897         if(this.fireEvent('beforeselect', this, v) !== false){
18898             var d = new Date(this.parseDate(v) ).clearTime();
18899         
18900             if(isNaN(d.getTime())){
18901                 this.date = this.viewDate = '';
18902                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18903                 return;
18904             }
18905
18906             v = this.formatDate(d);
18907
18908             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18909
18910             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18911
18912             this.update();
18913
18914             this.fireEvent('select', this, this.date);
18915         }
18916     },
18917     
18918     getValue: function()
18919     {
18920         return this.formatDate(this.date);
18921     },
18922     
18923     fireKey: function(e)
18924     {
18925         if (!this.picker().isVisible()){
18926             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18927                 this.show();
18928             }
18929             return;
18930         }
18931         
18932         var dateChanged = false,
18933         dir, day, month,
18934         newDate, newViewDate;
18935         
18936         switch(e.keyCode){
18937             case 27: // escape
18938                 this.hide();
18939                 e.preventDefault();
18940                 break;
18941             case 37: // left
18942             case 39: // right
18943                 if (!this.keyboardNavigation) {
18944                     break;
18945                 }
18946                 dir = e.keyCode == 37 ? -1 : 1;
18947                 
18948                 if (e.ctrlKey){
18949                     newDate = this.moveYear(this.date, dir);
18950                     newViewDate = this.moveYear(this.viewDate, dir);
18951                 } else if (e.shiftKey){
18952                     newDate = this.moveMonth(this.date, dir);
18953                     newViewDate = this.moveMonth(this.viewDate, dir);
18954                 } else {
18955                     newDate = new Date(this.date);
18956                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18957                     newViewDate = new Date(this.viewDate);
18958                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18959                 }
18960                 if (this.dateWithinRange(newDate)){
18961                     this.date = newDate;
18962                     this.viewDate = newViewDate;
18963                     this.setValue(this.formatDate(this.date));
18964 //                    this.update();
18965                     e.preventDefault();
18966                     dateChanged = true;
18967                 }
18968                 break;
18969             case 38: // up
18970             case 40: // down
18971                 if (!this.keyboardNavigation) {
18972                     break;
18973                 }
18974                 dir = e.keyCode == 38 ? -1 : 1;
18975                 if (e.ctrlKey){
18976                     newDate = this.moveYear(this.date, dir);
18977                     newViewDate = this.moveYear(this.viewDate, dir);
18978                 } else if (e.shiftKey){
18979                     newDate = this.moveMonth(this.date, dir);
18980                     newViewDate = this.moveMonth(this.viewDate, dir);
18981                 } else {
18982                     newDate = new Date(this.date);
18983                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18984                     newViewDate = new Date(this.viewDate);
18985                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18986                 }
18987                 if (this.dateWithinRange(newDate)){
18988                     this.date = newDate;
18989                     this.viewDate = newViewDate;
18990                     this.setValue(this.formatDate(this.date));
18991 //                    this.update();
18992                     e.preventDefault();
18993                     dateChanged = true;
18994                 }
18995                 break;
18996             case 13: // enter
18997                 this.setValue(this.formatDate(this.date));
18998                 this.hide();
18999                 e.preventDefault();
19000                 break;
19001             case 9: // tab
19002                 this.setValue(this.formatDate(this.date));
19003                 this.hide();
19004                 break;
19005             case 16: // shift
19006             case 17: // ctrl
19007             case 18: // alt
19008                 break;
19009             default :
19010                 this.hide();
19011                 
19012         }
19013     },
19014     
19015     
19016     onClick: function(e) 
19017     {
19018         e.stopPropagation();
19019         e.preventDefault();
19020         
19021         var target = e.getTarget();
19022         
19023         if(target.nodeName.toLowerCase() === 'i'){
19024             target = Roo.get(target).dom.parentNode;
19025         }
19026         
19027         var nodeName = target.nodeName;
19028         var className = target.className;
19029         var html = target.innerHTML;
19030         //Roo.log(nodeName);
19031         
19032         switch(nodeName.toLowerCase()) {
19033             case 'th':
19034                 switch(className) {
19035                     case 'switch':
19036                         this.showMode(1);
19037                         break;
19038                     case 'prev':
19039                     case 'next':
19040                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19041                         switch(this.viewMode){
19042                                 case 0:
19043                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19044                                         break;
19045                                 case 1:
19046                                 case 2:
19047                                         this.viewDate = this.moveYear(this.viewDate, dir);
19048                                         break;
19049                         }
19050                         this.fill();
19051                         break;
19052                     case 'today':
19053                         var date = new Date();
19054                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19055 //                        this.fill()
19056                         this.setValue(this.formatDate(this.date));
19057                         
19058                         this.hide();
19059                         break;
19060                 }
19061                 break;
19062             case 'span':
19063                 if (className.indexOf('disabled') < 0) {
19064                     this.viewDate.setUTCDate(1);
19065                     if (className.indexOf('month') > -1) {
19066                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19067                     } else {
19068                         var year = parseInt(html, 10) || 0;
19069                         this.viewDate.setUTCFullYear(year);
19070                         
19071                     }
19072                     
19073                     if(this.singleMode){
19074                         this.setValue(this.formatDate(this.viewDate));
19075                         this.hide();
19076                         return;
19077                     }
19078                     
19079                     this.showMode(-1);
19080                     this.fill();
19081                 }
19082                 break;
19083                 
19084             case 'td':
19085                 //Roo.log(className);
19086                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19087                     var day = parseInt(html, 10) || 1;
19088                     var year = this.viewDate.getUTCFullYear(),
19089                         month = this.viewDate.getUTCMonth();
19090
19091                     if (className.indexOf('old') > -1) {
19092                         if(month === 0 ){
19093                             month = 11;
19094                             year -= 1;
19095                         }else{
19096                             month -= 1;
19097                         }
19098                     } else if (className.indexOf('new') > -1) {
19099                         if (month == 11) {
19100                             month = 0;
19101                             year += 1;
19102                         } else {
19103                             month += 1;
19104                         }
19105                     }
19106                     //Roo.log([year,month,day]);
19107                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19108                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19109 //                    this.fill();
19110                     //Roo.log(this.formatDate(this.date));
19111                     this.setValue(this.formatDate(this.date));
19112                     this.hide();
19113                 }
19114                 break;
19115         }
19116     },
19117     
19118     setStartDate: function(startDate)
19119     {
19120         this.startDate = startDate || -Infinity;
19121         if (this.startDate !== -Infinity) {
19122             this.startDate = this.parseDate(this.startDate);
19123         }
19124         this.update();
19125         this.updateNavArrows();
19126     },
19127
19128     setEndDate: function(endDate)
19129     {
19130         this.endDate = endDate || Infinity;
19131         if (this.endDate !== Infinity) {
19132             this.endDate = this.parseDate(this.endDate);
19133         }
19134         this.update();
19135         this.updateNavArrows();
19136     },
19137     
19138     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19139     {
19140         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19141         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19142             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19143         }
19144         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19145             return parseInt(d, 10);
19146         });
19147         this.update();
19148         this.updateNavArrows();
19149     },
19150     
19151     updateNavArrows: function() 
19152     {
19153         if(this.singleMode){
19154             return;
19155         }
19156         
19157         var d = new Date(this.viewDate),
19158         year = d.getUTCFullYear(),
19159         month = d.getUTCMonth();
19160         
19161         Roo.each(this.picker().select('.prev', true).elements, function(v){
19162             v.show();
19163             switch (this.viewMode) {
19164                 case 0:
19165
19166                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19167                         v.hide();
19168                     }
19169                     break;
19170                 case 1:
19171                 case 2:
19172                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19173                         v.hide();
19174                     }
19175                     break;
19176             }
19177         });
19178         
19179         Roo.each(this.picker().select('.next', true).elements, function(v){
19180             v.show();
19181             switch (this.viewMode) {
19182                 case 0:
19183
19184                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19185                         v.hide();
19186                     }
19187                     break;
19188                 case 1:
19189                 case 2:
19190                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19191                         v.hide();
19192                     }
19193                     break;
19194             }
19195         })
19196     },
19197     
19198     moveMonth: function(date, dir)
19199     {
19200         if (!dir) {
19201             return date;
19202         }
19203         var new_date = new Date(date.valueOf()),
19204         day = new_date.getUTCDate(),
19205         month = new_date.getUTCMonth(),
19206         mag = Math.abs(dir),
19207         new_month, test;
19208         dir = dir > 0 ? 1 : -1;
19209         if (mag == 1){
19210             test = dir == -1
19211             // If going back one month, make sure month is not current month
19212             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19213             ? function(){
19214                 return new_date.getUTCMonth() == month;
19215             }
19216             // If going forward one month, make sure month is as expected
19217             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19218             : function(){
19219                 return new_date.getUTCMonth() != new_month;
19220             };
19221             new_month = month + dir;
19222             new_date.setUTCMonth(new_month);
19223             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19224             if (new_month < 0 || new_month > 11) {
19225                 new_month = (new_month + 12) % 12;
19226             }
19227         } else {
19228             // For magnitudes >1, move one month at a time...
19229             for (var i=0; i<mag; i++) {
19230                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19231                 new_date = this.moveMonth(new_date, dir);
19232             }
19233             // ...then reset the day, keeping it in the new month
19234             new_month = new_date.getUTCMonth();
19235             new_date.setUTCDate(day);
19236             test = function(){
19237                 return new_month != new_date.getUTCMonth();
19238             };
19239         }
19240         // Common date-resetting loop -- if date is beyond end of month, make it
19241         // end of month
19242         while (test()){
19243             new_date.setUTCDate(--day);
19244             new_date.setUTCMonth(new_month);
19245         }
19246         return new_date;
19247     },
19248
19249     moveYear: function(date, dir)
19250     {
19251         return this.moveMonth(date, dir*12);
19252     },
19253
19254     dateWithinRange: function(date)
19255     {
19256         return date >= this.startDate && date <= this.endDate;
19257     },
19258
19259     
19260     remove: function() 
19261     {
19262         this.picker().remove();
19263     },
19264     
19265     validateValue : function(value)
19266     {
19267         if(this.getVisibilityEl().hasClass('hidden')){
19268             return true;
19269         }
19270         
19271         if(value.length < 1)  {
19272             if(this.allowBlank){
19273                 return true;
19274             }
19275             return false;
19276         }
19277         
19278         if(value.length < this.minLength){
19279             return false;
19280         }
19281         if(value.length > this.maxLength){
19282             return false;
19283         }
19284         if(this.vtype){
19285             var vt = Roo.form.VTypes;
19286             if(!vt[this.vtype](value, this)){
19287                 return false;
19288             }
19289         }
19290         if(typeof this.validator == "function"){
19291             var msg = this.validator(value);
19292             if(msg !== true){
19293                 return false;
19294             }
19295         }
19296         
19297         if(this.regex && !this.regex.test(value)){
19298             return false;
19299         }
19300         
19301         if(typeof(this.parseDate(value)) == 'undefined'){
19302             return false;
19303         }
19304         
19305         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19306             return false;
19307         }      
19308         
19309         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19310             return false;
19311         } 
19312         
19313         
19314         return true;
19315     },
19316     
19317     reset : function()
19318     {
19319         this.date = this.viewDate = '';
19320         
19321         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19322     }
19323    
19324 });
19325
19326 Roo.apply(Roo.bootstrap.DateField,  {
19327     
19328     head : {
19329         tag: 'thead',
19330         cn: [
19331         {
19332             tag: 'tr',
19333             cn: [
19334             {
19335                 tag: 'th',
19336                 cls: 'prev',
19337                 html: '<i class="fa fa-arrow-left"/>'
19338             },
19339             {
19340                 tag: 'th',
19341                 cls: 'switch',
19342                 colspan: '5'
19343             },
19344             {
19345                 tag: 'th',
19346                 cls: 'next',
19347                 html: '<i class="fa fa-arrow-right"/>'
19348             }
19349
19350             ]
19351         }
19352         ]
19353     },
19354     
19355     content : {
19356         tag: 'tbody',
19357         cn: [
19358         {
19359             tag: 'tr',
19360             cn: [
19361             {
19362                 tag: 'td',
19363                 colspan: '7'
19364             }
19365             ]
19366         }
19367         ]
19368     },
19369     
19370     footer : {
19371         tag: 'tfoot',
19372         cn: [
19373         {
19374             tag: 'tr',
19375             cn: [
19376             {
19377                 tag: 'th',
19378                 colspan: '7',
19379                 cls: 'today'
19380             }
19381                     
19382             ]
19383         }
19384         ]
19385     },
19386     
19387     dates:{
19388         en: {
19389             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19390             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19391             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19392             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19393             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19394             today: "Today"
19395         }
19396     },
19397     
19398     modes: [
19399     {
19400         clsName: 'days',
19401         navFnc: 'Month',
19402         navStep: 1
19403     },
19404     {
19405         clsName: 'months',
19406         navFnc: 'FullYear',
19407         navStep: 1
19408     },
19409     {
19410         clsName: 'years',
19411         navFnc: 'FullYear',
19412         navStep: 10
19413     }]
19414 });
19415
19416 Roo.apply(Roo.bootstrap.DateField,  {
19417   
19418     template : {
19419         tag: 'div',
19420         cls: 'datepicker dropdown-menu roo-dynamic',
19421         cn: [
19422         {
19423             tag: 'div',
19424             cls: 'datepicker-days',
19425             cn: [
19426             {
19427                 tag: 'table',
19428                 cls: 'table-condensed',
19429                 cn:[
19430                 Roo.bootstrap.DateField.head,
19431                 {
19432                     tag: 'tbody'
19433                 },
19434                 Roo.bootstrap.DateField.footer
19435                 ]
19436             }
19437             ]
19438         },
19439         {
19440             tag: 'div',
19441             cls: 'datepicker-months',
19442             cn: [
19443             {
19444                 tag: 'table',
19445                 cls: 'table-condensed',
19446                 cn:[
19447                 Roo.bootstrap.DateField.head,
19448                 Roo.bootstrap.DateField.content,
19449                 Roo.bootstrap.DateField.footer
19450                 ]
19451             }
19452             ]
19453         },
19454         {
19455             tag: 'div',
19456             cls: 'datepicker-years',
19457             cn: [
19458             {
19459                 tag: 'table',
19460                 cls: 'table-condensed',
19461                 cn:[
19462                 Roo.bootstrap.DateField.head,
19463                 Roo.bootstrap.DateField.content,
19464                 Roo.bootstrap.DateField.footer
19465                 ]
19466             }
19467             ]
19468         }
19469         ]
19470     }
19471 });
19472
19473  
19474
19475  /*
19476  * - LGPL
19477  *
19478  * TimeField
19479  * 
19480  */
19481
19482 /**
19483  * @class Roo.bootstrap.TimeField
19484  * @extends Roo.bootstrap.Input
19485  * Bootstrap DateField class
19486  * 
19487  * 
19488  * @constructor
19489  * Create a new TimeField
19490  * @param {Object} config The config object
19491  */
19492
19493 Roo.bootstrap.TimeField = function(config){
19494     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19495     this.addEvents({
19496             /**
19497              * @event show
19498              * Fires when this field show.
19499              * @param {Roo.bootstrap.DateField} thisthis
19500              * @param {Mixed} date The date value
19501              */
19502             show : true,
19503             /**
19504              * @event show
19505              * Fires when this field hide.
19506              * @param {Roo.bootstrap.DateField} this
19507              * @param {Mixed} date The date value
19508              */
19509             hide : true,
19510             /**
19511              * @event select
19512              * Fires when select a date.
19513              * @param {Roo.bootstrap.DateField} this
19514              * @param {Mixed} date The date value
19515              */
19516             select : true
19517         });
19518 };
19519
19520 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19521     
19522     /**
19523      * @cfg {String} format
19524      * The default time format string which can be overriden for localization support.  The format must be
19525      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19526      */
19527     format : "H:i",
19528        
19529     onRender: function(ct, position)
19530     {
19531         
19532         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19533                 
19534         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19535         
19536         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19537         
19538         this.pop = this.picker().select('>.datepicker-time',true).first();
19539         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19540         
19541         this.picker().on('mousedown', this.onMousedown, this);
19542         this.picker().on('click', this.onClick, this);
19543         
19544         this.picker().addClass('datepicker-dropdown');
19545     
19546         this.fillTime();
19547         this.update();
19548             
19549         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19550         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19551         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19552         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19553         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19554         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19555
19556     },
19557     
19558     fireKey: function(e){
19559         if (!this.picker().isVisible()){
19560             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19561                 this.show();
19562             }
19563             return;
19564         }
19565
19566         e.preventDefault();
19567         
19568         switch(e.keyCode){
19569             case 27: // escape
19570                 this.hide();
19571                 break;
19572             case 37: // left
19573             case 39: // right
19574                 this.onTogglePeriod();
19575                 break;
19576             case 38: // up
19577                 this.onIncrementMinutes();
19578                 break;
19579             case 40: // down
19580                 this.onDecrementMinutes();
19581                 break;
19582             case 13: // enter
19583             case 9: // tab
19584                 this.setTime();
19585                 break;
19586         }
19587     },
19588     
19589     onClick: function(e) {
19590         e.stopPropagation();
19591         e.preventDefault();
19592     },
19593     
19594     picker : function()
19595     {
19596         return this.el.select('.datepicker', true).first();
19597     },
19598     
19599     fillTime: function()
19600     {    
19601         var time = this.pop.select('tbody', true).first();
19602         
19603         time.dom.innerHTML = '';
19604         
19605         time.createChild({
19606             tag: 'tr',
19607             cn: [
19608                 {
19609                     tag: 'td',
19610                     cn: [
19611                         {
19612                             tag: 'a',
19613                             href: '#',
19614                             cls: 'btn',
19615                             cn: [
19616                                 {
19617                                     tag: 'span',
19618                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19619                                 }
19620                             ]
19621                         } 
19622                     ]
19623                 },
19624                 {
19625                     tag: 'td',
19626                     cls: 'separator'
19627                 },
19628                 {
19629                     tag: 'td',
19630                     cn: [
19631                         {
19632                             tag: 'a',
19633                             href: '#',
19634                             cls: 'btn',
19635                             cn: [
19636                                 {
19637                                     tag: 'span',
19638                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19639                                 }
19640                             ]
19641                         }
19642                     ]
19643                 },
19644                 {
19645                     tag: 'td',
19646                     cls: 'separator'
19647                 }
19648             ]
19649         });
19650         
19651         time.createChild({
19652             tag: 'tr',
19653             cn: [
19654                 {
19655                     tag: 'td',
19656                     cn: [
19657                         {
19658                             tag: 'span',
19659                             cls: 'timepicker-hour',
19660                             html: '00'
19661                         }  
19662                     ]
19663                 },
19664                 {
19665                     tag: 'td',
19666                     cls: 'separator',
19667                     html: ':'
19668                 },
19669                 {
19670                     tag: 'td',
19671                     cn: [
19672                         {
19673                             tag: 'span',
19674                             cls: 'timepicker-minute',
19675                             html: '00'
19676                         }  
19677                     ]
19678                 },
19679                 {
19680                     tag: 'td',
19681                     cls: 'separator'
19682                 },
19683                 {
19684                     tag: 'td',
19685                     cn: [
19686                         {
19687                             tag: 'button',
19688                             type: 'button',
19689                             cls: 'btn btn-primary period',
19690                             html: 'AM'
19691                             
19692                         }
19693                     ]
19694                 }
19695             ]
19696         });
19697         
19698         time.createChild({
19699             tag: 'tr',
19700             cn: [
19701                 {
19702                     tag: 'td',
19703                     cn: [
19704                         {
19705                             tag: 'a',
19706                             href: '#',
19707                             cls: 'btn',
19708                             cn: [
19709                                 {
19710                                     tag: 'span',
19711                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19712                                 }
19713                             ]
19714                         }
19715                     ]
19716                 },
19717                 {
19718                     tag: 'td',
19719                     cls: 'separator'
19720                 },
19721                 {
19722                     tag: 'td',
19723                     cn: [
19724                         {
19725                             tag: 'a',
19726                             href: '#',
19727                             cls: 'btn',
19728                             cn: [
19729                                 {
19730                                     tag: 'span',
19731                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19732                                 }
19733                             ]
19734                         }
19735                     ]
19736                 },
19737                 {
19738                     tag: 'td',
19739                     cls: 'separator'
19740                 }
19741             ]
19742         });
19743         
19744     },
19745     
19746     update: function()
19747     {
19748         
19749         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19750         
19751         this.fill();
19752     },
19753     
19754     fill: function() 
19755     {
19756         var hours = this.time.getHours();
19757         var minutes = this.time.getMinutes();
19758         var period = 'AM';
19759         
19760         if(hours > 11){
19761             period = 'PM';
19762         }
19763         
19764         if(hours == 0){
19765             hours = 12;
19766         }
19767         
19768         
19769         if(hours > 12){
19770             hours = hours - 12;
19771         }
19772         
19773         if(hours < 10){
19774             hours = '0' + hours;
19775         }
19776         
19777         if(minutes < 10){
19778             minutes = '0' + minutes;
19779         }
19780         
19781         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19782         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19783         this.pop.select('button', true).first().dom.innerHTML = period;
19784         
19785     },
19786     
19787     place: function()
19788     {   
19789         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19790         
19791         var cls = ['bottom'];
19792         
19793         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19794             cls.pop();
19795             cls.push('top');
19796         }
19797         
19798         cls.push('right');
19799         
19800         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19801             cls.pop();
19802             cls.push('left');
19803         }
19804         
19805         this.picker().addClass(cls.join('-'));
19806         
19807         var _this = this;
19808         
19809         Roo.each(cls, function(c){
19810             if(c == 'bottom'){
19811                 _this.picker().setTop(_this.inputEl().getHeight());
19812                 return;
19813             }
19814             if(c == 'top'){
19815                 _this.picker().setTop(0 - _this.picker().getHeight());
19816                 return;
19817             }
19818             
19819             if(c == 'left'){
19820                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19821                 return;
19822             }
19823             if(c == 'right'){
19824                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19825                 return;
19826             }
19827         });
19828         
19829     },
19830   
19831     onFocus : function()
19832     {
19833         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19834         this.show();
19835     },
19836     
19837     onBlur : function()
19838     {
19839         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19840         this.hide();
19841     },
19842     
19843     show : function()
19844     {
19845         this.picker().show();
19846         this.pop.show();
19847         this.update();
19848         this.place();
19849         
19850         this.fireEvent('show', this, this.date);
19851     },
19852     
19853     hide : function()
19854     {
19855         this.picker().hide();
19856         this.pop.hide();
19857         
19858         this.fireEvent('hide', this, this.date);
19859     },
19860     
19861     setTime : function()
19862     {
19863         this.hide();
19864         this.setValue(this.time.format(this.format));
19865         
19866         this.fireEvent('select', this, this.date);
19867         
19868         
19869     },
19870     
19871     onMousedown: function(e){
19872         e.stopPropagation();
19873         e.preventDefault();
19874     },
19875     
19876     onIncrementHours: function()
19877     {
19878         Roo.log('onIncrementHours');
19879         this.time = this.time.add(Date.HOUR, 1);
19880         this.update();
19881         
19882     },
19883     
19884     onDecrementHours: function()
19885     {
19886         Roo.log('onDecrementHours');
19887         this.time = this.time.add(Date.HOUR, -1);
19888         this.update();
19889     },
19890     
19891     onIncrementMinutes: function()
19892     {
19893         Roo.log('onIncrementMinutes');
19894         this.time = this.time.add(Date.MINUTE, 1);
19895         this.update();
19896     },
19897     
19898     onDecrementMinutes: function()
19899     {
19900         Roo.log('onDecrementMinutes');
19901         this.time = this.time.add(Date.MINUTE, -1);
19902         this.update();
19903     },
19904     
19905     onTogglePeriod: function()
19906     {
19907         Roo.log('onTogglePeriod');
19908         this.time = this.time.add(Date.HOUR, 12);
19909         this.update();
19910     }
19911     
19912    
19913 });
19914
19915 Roo.apply(Roo.bootstrap.TimeField,  {
19916     
19917     content : {
19918         tag: 'tbody',
19919         cn: [
19920             {
19921                 tag: 'tr',
19922                 cn: [
19923                 {
19924                     tag: 'td',
19925                     colspan: '7'
19926                 }
19927                 ]
19928             }
19929         ]
19930     },
19931     
19932     footer : {
19933         tag: 'tfoot',
19934         cn: [
19935             {
19936                 tag: 'tr',
19937                 cn: [
19938                 {
19939                     tag: 'th',
19940                     colspan: '7',
19941                     cls: '',
19942                     cn: [
19943                         {
19944                             tag: 'button',
19945                             cls: 'btn btn-info ok',
19946                             html: 'OK'
19947                         }
19948                     ]
19949                 }
19950
19951                 ]
19952             }
19953         ]
19954     }
19955 });
19956
19957 Roo.apply(Roo.bootstrap.TimeField,  {
19958   
19959     template : {
19960         tag: 'div',
19961         cls: 'datepicker dropdown-menu',
19962         cn: [
19963             {
19964                 tag: 'div',
19965                 cls: 'datepicker-time',
19966                 cn: [
19967                 {
19968                     tag: 'table',
19969                     cls: 'table-condensed',
19970                     cn:[
19971                     Roo.bootstrap.TimeField.content,
19972                     Roo.bootstrap.TimeField.footer
19973                     ]
19974                 }
19975                 ]
19976             }
19977         ]
19978     }
19979 });
19980
19981  
19982
19983  /*
19984  * - LGPL
19985  *
19986  * MonthField
19987  * 
19988  */
19989
19990 /**
19991  * @class Roo.bootstrap.MonthField
19992  * @extends Roo.bootstrap.Input
19993  * Bootstrap MonthField class
19994  * 
19995  * @cfg {String} language default en
19996  * 
19997  * @constructor
19998  * Create a new MonthField
19999  * @param {Object} config The config object
20000  */
20001
20002 Roo.bootstrap.MonthField = function(config){
20003     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20004     
20005     this.addEvents({
20006         /**
20007          * @event show
20008          * Fires when this field show.
20009          * @param {Roo.bootstrap.MonthField} this
20010          * @param {Mixed} date The date value
20011          */
20012         show : true,
20013         /**
20014          * @event show
20015          * Fires when this field hide.
20016          * @param {Roo.bootstrap.MonthField} this
20017          * @param {Mixed} date The date value
20018          */
20019         hide : true,
20020         /**
20021          * @event select
20022          * Fires when select a date.
20023          * @param {Roo.bootstrap.MonthField} this
20024          * @param {String} oldvalue The old value
20025          * @param {String} newvalue The new value
20026          */
20027         select : true
20028     });
20029 };
20030
20031 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20032     
20033     onRender: function(ct, position)
20034     {
20035         
20036         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20037         
20038         this.language = this.language || 'en';
20039         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20040         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20041         
20042         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20043         this.isInline = false;
20044         this.isInput = true;
20045         this.component = this.el.select('.add-on', true).first() || false;
20046         this.component = (this.component && this.component.length === 0) ? false : this.component;
20047         this.hasInput = this.component && this.inputEL().length;
20048         
20049         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20050         
20051         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20052         
20053         this.picker().on('mousedown', this.onMousedown, this);
20054         this.picker().on('click', this.onClick, this);
20055         
20056         this.picker().addClass('datepicker-dropdown');
20057         
20058         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20059             v.setStyle('width', '189px');
20060         });
20061         
20062         this.fillMonths();
20063         
20064         this.update();
20065         
20066         if(this.isInline) {
20067             this.show();
20068         }
20069         
20070     },
20071     
20072     setValue: function(v, suppressEvent)
20073     {   
20074         var o = this.getValue();
20075         
20076         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20077         
20078         this.update();
20079
20080         if(suppressEvent !== true){
20081             this.fireEvent('select', this, o, v);
20082         }
20083         
20084     },
20085     
20086     getValue: function()
20087     {
20088         return this.value;
20089     },
20090     
20091     onClick: function(e) 
20092     {
20093         e.stopPropagation();
20094         e.preventDefault();
20095         
20096         var target = e.getTarget();
20097         
20098         if(target.nodeName.toLowerCase() === 'i'){
20099             target = Roo.get(target).dom.parentNode;
20100         }
20101         
20102         var nodeName = target.nodeName;
20103         var className = target.className;
20104         var html = target.innerHTML;
20105         
20106         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20107             return;
20108         }
20109         
20110         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20111         
20112         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20113         
20114         this.hide();
20115                         
20116     },
20117     
20118     picker : function()
20119     {
20120         return this.pickerEl;
20121     },
20122     
20123     fillMonths: function()
20124     {    
20125         var i = 0;
20126         var months = this.picker().select('>.datepicker-months td', true).first();
20127         
20128         months.dom.innerHTML = '';
20129         
20130         while (i < 12) {
20131             var month = {
20132                 tag: 'span',
20133                 cls: 'month',
20134                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20135             };
20136             
20137             months.createChild(month);
20138         }
20139         
20140     },
20141     
20142     update: function()
20143     {
20144         var _this = this;
20145         
20146         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20147             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20148         }
20149         
20150         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20151             e.removeClass('active');
20152             
20153             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20154                 e.addClass('active');
20155             }
20156         })
20157     },
20158     
20159     place: function()
20160     {
20161         if(this.isInline) {
20162             return;
20163         }
20164         
20165         this.picker().removeClass(['bottom', 'top']);
20166         
20167         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20168             /*
20169              * place to the top of element!
20170              *
20171              */
20172             
20173             this.picker().addClass('top');
20174             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20175             
20176             return;
20177         }
20178         
20179         this.picker().addClass('bottom');
20180         
20181         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20182     },
20183     
20184     onFocus : function()
20185     {
20186         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20187         this.show();
20188     },
20189     
20190     onBlur : function()
20191     {
20192         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20193         
20194         var d = this.inputEl().getValue();
20195         
20196         this.setValue(d);
20197                 
20198         this.hide();
20199     },
20200     
20201     show : function()
20202     {
20203         this.picker().show();
20204         this.picker().select('>.datepicker-months', true).first().show();
20205         this.update();
20206         this.place();
20207         
20208         this.fireEvent('show', this, this.date);
20209     },
20210     
20211     hide : function()
20212     {
20213         if(this.isInline) {
20214             return;
20215         }
20216         this.picker().hide();
20217         this.fireEvent('hide', this, this.date);
20218         
20219     },
20220     
20221     onMousedown: function(e)
20222     {
20223         e.stopPropagation();
20224         e.preventDefault();
20225     },
20226     
20227     keyup: function(e)
20228     {
20229         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20230         this.update();
20231     },
20232
20233     fireKey: function(e)
20234     {
20235         if (!this.picker().isVisible()){
20236             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20237                 this.show();
20238             }
20239             return;
20240         }
20241         
20242         var dir;
20243         
20244         switch(e.keyCode){
20245             case 27: // escape
20246                 this.hide();
20247                 e.preventDefault();
20248                 break;
20249             case 37: // left
20250             case 39: // right
20251                 dir = e.keyCode == 37 ? -1 : 1;
20252                 
20253                 this.vIndex = this.vIndex + dir;
20254                 
20255                 if(this.vIndex < 0){
20256                     this.vIndex = 0;
20257                 }
20258                 
20259                 if(this.vIndex > 11){
20260                     this.vIndex = 11;
20261                 }
20262                 
20263                 if(isNaN(this.vIndex)){
20264                     this.vIndex = 0;
20265                 }
20266                 
20267                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20268                 
20269                 break;
20270             case 38: // up
20271             case 40: // down
20272                 
20273                 dir = e.keyCode == 38 ? -1 : 1;
20274                 
20275                 this.vIndex = this.vIndex + dir * 4;
20276                 
20277                 if(this.vIndex < 0){
20278                     this.vIndex = 0;
20279                 }
20280                 
20281                 if(this.vIndex > 11){
20282                     this.vIndex = 11;
20283                 }
20284                 
20285                 if(isNaN(this.vIndex)){
20286                     this.vIndex = 0;
20287                 }
20288                 
20289                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20290                 break;
20291                 
20292             case 13: // enter
20293                 
20294                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20295                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20296                 }
20297                 
20298                 this.hide();
20299                 e.preventDefault();
20300                 break;
20301             case 9: // tab
20302                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20303                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20304                 }
20305                 this.hide();
20306                 break;
20307             case 16: // shift
20308             case 17: // ctrl
20309             case 18: // alt
20310                 break;
20311             default :
20312                 this.hide();
20313                 
20314         }
20315     },
20316     
20317     remove: function() 
20318     {
20319         this.picker().remove();
20320     }
20321    
20322 });
20323
20324 Roo.apply(Roo.bootstrap.MonthField,  {
20325     
20326     content : {
20327         tag: 'tbody',
20328         cn: [
20329         {
20330             tag: 'tr',
20331             cn: [
20332             {
20333                 tag: 'td',
20334                 colspan: '7'
20335             }
20336             ]
20337         }
20338         ]
20339     },
20340     
20341     dates:{
20342         en: {
20343             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20344             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20345         }
20346     }
20347 });
20348
20349 Roo.apply(Roo.bootstrap.MonthField,  {
20350   
20351     template : {
20352         tag: 'div',
20353         cls: 'datepicker dropdown-menu roo-dynamic',
20354         cn: [
20355             {
20356                 tag: 'div',
20357                 cls: 'datepicker-months',
20358                 cn: [
20359                 {
20360                     tag: 'table',
20361                     cls: 'table-condensed',
20362                     cn:[
20363                         Roo.bootstrap.DateField.content
20364                     ]
20365                 }
20366                 ]
20367             }
20368         ]
20369     }
20370 });
20371
20372  
20373
20374  
20375  /*
20376  * - LGPL
20377  *
20378  * CheckBox
20379  * 
20380  */
20381
20382 /**
20383  * @class Roo.bootstrap.CheckBox
20384  * @extends Roo.bootstrap.Input
20385  * Bootstrap CheckBox class
20386  * 
20387  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20388  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20389  * @cfg {String} boxLabel The text that appears beside the checkbox
20390  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20391  * @cfg {Boolean} checked initnal the element
20392  * @cfg {Boolean} inline inline the element (default false)
20393  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20394  * @cfg {String} tooltip label tooltip
20395  * 
20396  * @constructor
20397  * Create a new CheckBox
20398  * @param {Object} config The config object
20399  */
20400
20401 Roo.bootstrap.CheckBox = function(config){
20402     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20403    
20404     this.addEvents({
20405         /**
20406         * @event check
20407         * Fires when the element is checked or unchecked.
20408         * @param {Roo.bootstrap.CheckBox} this This input
20409         * @param {Boolean} checked The new checked value
20410         */
20411        check : true,
20412        /**
20413         * @event click
20414         * Fires when the element is click.
20415         * @param {Roo.bootstrap.CheckBox} this This input
20416         */
20417        click : true
20418     });
20419     
20420 };
20421
20422 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20423   
20424     inputType: 'checkbox',
20425     inputValue: 1,
20426     valueOff: 0,
20427     boxLabel: false,
20428     checked: false,
20429     weight : false,
20430     inline: false,
20431     tooltip : '',
20432     
20433     getAutoCreate : function()
20434     {
20435         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20436         
20437         var id = Roo.id();
20438         
20439         var cfg = {};
20440         
20441         cfg.cls = 'form-group ' + this.inputType; //input-group
20442         
20443         if(this.inline){
20444             cfg.cls += ' ' + this.inputType + '-inline';
20445         }
20446         
20447         var input =  {
20448             tag: 'input',
20449             id : id,
20450             type : this.inputType,
20451             value : this.inputValue,
20452             cls : 'roo-' + this.inputType, //'form-box',
20453             placeholder : this.placeholder || ''
20454             
20455         };
20456         
20457         if(this.inputType != 'radio'){
20458             var hidden =  {
20459                 tag: 'input',
20460                 type : 'hidden',
20461                 cls : 'roo-hidden-value',
20462                 value : this.checked ? this.inputValue : this.valueOff
20463             };
20464         }
20465         
20466             
20467         if (this.weight) { // Validity check?
20468             cfg.cls += " " + this.inputType + "-" + this.weight;
20469         }
20470         
20471         if (this.disabled) {
20472             input.disabled=true;
20473         }
20474         
20475         if(this.checked){
20476             input.checked = this.checked;
20477         }
20478         
20479         if (this.name) {
20480             
20481             input.name = this.name;
20482             
20483             if(this.inputType != 'radio'){
20484                 hidden.name = this.name;
20485                 input.name = '_hidden_' + this.name;
20486             }
20487         }
20488         
20489         if (this.size) {
20490             input.cls += ' input-' + this.size;
20491         }
20492         
20493         var settings=this;
20494         
20495         ['xs','sm','md','lg'].map(function(size){
20496             if (settings[size]) {
20497                 cfg.cls += ' col-' + size + '-' + settings[size];
20498             }
20499         });
20500         
20501         var inputblock = input;
20502          
20503         if (this.before || this.after) {
20504             
20505             inputblock = {
20506                 cls : 'input-group',
20507                 cn :  [] 
20508             };
20509             
20510             if (this.before) {
20511                 inputblock.cn.push({
20512                     tag :'span',
20513                     cls : 'input-group-addon',
20514                     html : this.before
20515                 });
20516             }
20517             
20518             inputblock.cn.push(input);
20519             
20520             if(this.inputType != 'radio'){
20521                 inputblock.cn.push(hidden);
20522             }
20523             
20524             if (this.after) {
20525                 inputblock.cn.push({
20526                     tag :'span',
20527                     cls : 'input-group-addon',
20528                     html : this.after
20529                 });
20530             }
20531             
20532         }
20533         
20534         if (align ==='left' && this.fieldLabel.length) {
20535 //                Roo.log("left and has label");
20536             cfg.cn = [
20537                 {
20538                     tag: 'label',
20539                     'for' :  id,
20540                     cls : 'control-label',
20541                     html : this.fieldLabel
20542                 },
20543                 {
20544                     cls : "", 
20545                     cn: [
20546                         inputblock
20547                     ]
20548                 }
20549             ];
20550             
20551             if(this.labelWidth > 12){
20552                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20553             }
20554             
20555             if(this.labelWidth < 13 && this.labelmd == 0){
20556                 this.labelmd = this.labelWidth;
20557             }
20558             
20559             if(this.labellg > 0){
20560                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20561                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20562             }
20563             
20564             if(this.labelmd > 0){
20565                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20566                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20567             }
20568             
20569             if(this.labelsm > 0){
20570                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20571                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20572             }
20573             
20574             if(this.labelxs > 0){
20575                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20576                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20577             }
20578             
20579         } else if ( this.fieldLabel.length) {
20580 //                Roo.log(" label");
20581                 cfg.cn = [
20582                    
20583                     {
20584                         tag: this.boxLabel ? 'span' : 'label',
20585                         'for': id,
20586                         cls: 'control-label box-input-label',
20587                         //cls : 'input-group-addon',
20588                         html : this.fieldLabel
20589                     },
20590                     
20591                     inputblock
20592                     
20593                 ];
20594
20595         } else {
20596             
20597 //                Roo.log(" no label && no align");
20598                 cfg.cn = [  inputblock ] ;
20599                 
20600                 
20601         }
20602         
20603         if(this.boxLabel){
20604              var boxLabelCfg = {
20605                 tag: 'label',
20606                 //'for': id, // box label is handled by onclick - so no for...
20607                 cls: 'box-label',
20608                 html: this.boxLabel
20609             };
20610             
20611             if(this.tooltip){
20612                 boxLabelCfg.tooltip = this.tooltip;
20613             }
20614              
20615             cfg.cn.push(boxLabelCfg);
20616         }
20617         
20618         if(this.inputType != 'radio'){
20619             cfg.cn.push(hidden);
20620         }
20621         
20622         return cfg;
20623         
20624     },
20625     
20626     /**
20627      * return the real input element.
20628      */
20629     inputEl: function ()
20630     {
20631         return this.el.select('input.roo-' + this.inputType,true).first();
20632     },
20633     hiddenEl: function ()
20634     {
20635         return this.el.select('input.roo-hidden-value',true).first();
20636     },
20637     
20638     labelEl: function()
20639     {
20640         return this.el.select('label.control-label',true).first();
20641     },
20642     /* depricated... */
20643     
20644     label: function()
20645     {
20646         return this.labelEl();
20647     },
20648     
20649     boxLabelEl: function()
20650     {
20651         return this.el.select('label.box-label',true).first();
20652     },
20653     
20654     initEvents : function()
20655     {
20656 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20657         
20658         this.inputEl().on('click', this.onClick,  this);
20659         
20660         if (this.boxLabel) { 
20661             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20662         }
20663         
20664         this.startValue = this.getValue();
20665         
20666         if(this.groupId){
20667             Roo.bootstrap.CheckBox.register(this);
20668         }
20669     },
20670     
20671     onClick : function(e)
20672     {   
20673         if(this.fireEvent('click', this, e) !== false){
20674             this.setChecked(!this.checked);
20675         }
20676         
20677     },
20678     
20679     setChecked : function(state,suppressEvent)
20680     {
20681         this.startValue = this.getValue();
20682
20683         if(this.inputType == 'radio'){
20684             
20685             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20686                 e.dom.checked = false;
20687             });
20688             
20689             this.inputEl().dom.checked = true;
20690             
20691             this.inputEl().dom.value = this.inputValue;
20692             
20693             if(suppressEvent !== true){
20694                 this.fireEvent('check', this, true);
20695             }
20696             
20697             this.validate();
20698             
20699             return;
20700         }
20701         
20702         this.checked = state;
20703         
20704         this.inputEl().dom.checked = state;
20705         
20706         
20707         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20708         
20709         if(suppressEvent !== true){
20710             this.fireEvent('check', this, state);
20711         }
20712         
20713         this.validate();
20714     },
20715     
20716     getValue : function()
20717     {
20718         if(this.inputType == 'radio'){
20719             return this.getGroupValue();
20720         }
20721         
20722         return this.hiddenEl().dom.value;
20723         
20724     },
20725     
20726     getGroupValue : function()
20727     {
20728         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20729             return '';
20730         }
20731         
20732         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20733     },
20734     
20735     setValue : function(v,suppressEvent)
20736     {
20737         if(this.inputType == 'radio'){
20738             this.setGroupValue(v, suppressEvent);
20739             return;
20740         }
20741         
20742         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20743         
20744         this.validate();
20745     },
20746     
20747     setGroupValue : function(v, suppressEvent)
20748     {
20749         this.startValue = this.getValue();
20750         
20751         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20752             e.dom.checked = false;
20753             
20754             if(e.dom.value == v){
20755                 e.dom.checked = true;
20756             }
20757         });
20758         
20759         if(suppressEvent !== true){
20760             this.fireEvent('check', this, true);
20761         }
20762
20763         this.validate();
20764         
20765         return;
20766     },
20767     
20768     validate : function()
20769     {
20770         if(this.getVisibilityEl().hasClass('hidden')){
20771             return true;
20772         }
20773         
20774         if(
20775                 this.disabled || 
20776                 (this.inputType == 'radio' && this.validateRadio()) ||
20777                 (this.inputType == 'checkbox' && this.validateCheckbox())
20778         ){
20779             this.markValid();
20780             return true;
20781         }
20782         
20783         this.markInvalid();
20784         return false;
20785     },
20786     
20787     validateRadio : function()
20788     {
20789         if(this.getVisibilityEl().hasClass('hidden')){
20790             return true;
20791         }
20792         
20793         if(this.allowBlank){
20794             return true;
20795         }
20796         
20797         var valid = false;
20798         
20799         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20800             if(!e.dom.checked){
20801                 return;
20802             }
20803             
20804             valid = true;
20805             
20806             return false;
20807         });
20808         
20809         return valid;
20810     },
20811     
20812     validateCheckbox : function()
20813     {
20814         if(!this.groupId){
20815             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20816             //return (this.getValue() == this.inputValue) ? true : false;
20817         }
20818         
20819         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20820         
20821         if(!group){
20822             return false;
20823         }
20824         
20825         var r = false;
20826         
20827         for(var i in group){
20828             if(group[i].el.isVisible(true)){
20829                 r = false;
20830                 break;
20831             }
20832             
20833             r = true;
20834         }
20835         
20836         for(var i in group){
20837             if(r){
20838                 break;
20839             }
20840             
20841             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20842         }
20843         
20844         return r;
20845     },
20846     
20847     /**
20848      * Mark this field as valid
20849      */
20850     markValid : function()
20851     {
20852         var _this = this;
20853         
20854         this.fireEvent('valid', this);
20855         
20856         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20857         
20858         if(this.groupId){
20859             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20860         }
20861         
20862         if(label){
20863             label.markValid();
20864         }
20865
20866         if(this.inputType == 'radio'){
20867             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20868                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20869                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20870             });
20871             
20872             return;
20873         }
20874
20875         if(!this.groupId){
20876             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20877             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20878             return;
20879         }
20880         
20881         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20882         
20883         if(!group){
20884             return;
20885         }
20886         
20887         for(var i in group){
20888             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20889             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20890         }
20891     },
20892     
20893      /**
20894      * Mark this field as invalid
20895      * @param {String} msg The validation message
20896      */
20897     markInvalid : function(msg)
20898     {
20899         if(this.allowBlank){
20900             return;
20901         }
20902         
20903         var _this = this;
20904         
20905         this.fireEvent('invalid', this, msg);
20906         
20907         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20908         
20909         if(this.groupId){
20910             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20911         }
20912         
20913         if(label){
20914             label.markInvalid();
20915         }
20916             
20917         if(this.inputType == 'radio'){
20918             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20919                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20920                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20921             });
20922             
20923             return;
20924         }
20925         
20926         if(!this.groupId){
20927             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20928             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20929             return;
20930         }
20931         
20932         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20933         
20934         if(!group){
20935             return;
20936         }
20937         
20938         for(var i in group){
20939             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20940             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20941         }
20942         
20943     },
20944     
20945     clearInvalid : function()
20946     {
20947         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20948         
20949         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20950         
20951         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20952         
20953         if (label && label.iconEl) {
20954             label.iconEl.removeClass(label.validClass);
20955             label.iconEl.removeClass(label.invalidClass);
20956         }
20957     },
20958     
20959     disable : function()
20960     {
20961         if(this.inputType != 'radio'){
20962             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20963             return;
20964         }
20965         
20966         var _this = this;
20967         
20968         if(this.rendered){
20969             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20970                 _this.getActionEl().addClass(this.disabledClass);
20971                 e.dom.disabled = true;
20972             });
20973         }
20974         
20975         this.disabled = true;
20976         this.fireEvent("disable", this);
20977         return this;
20978     },
20979
20980     enable : function()
20981     {
20982         if(this.inputType != 'radio'){
20983             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20984             return;
20985         }
20986         
20987         var _this = this;
20988         
20989         if(this.rendered){
20990             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20991                 _this.getActionEl().removeClass(this.disabledClass);
20992                 e.dom.disabled = false;
20993             });
20994         }
20995         
20996         this.disabled = false;
20997         this.fireEvent("enable", this);
20998         return this;
20999     },
21000     
21001     setBoxLabel : function(v)
21002     {
21003         this.boxLabel = v;
21004         
21005         if(this.rendered){
21006             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21007         }
21008     }
21009
21010 });
21011
21012 Roo.apply(Roo.bootstrap.CheckBox, {
21013     
21014     groups: {},
21015     
21016      /**
21017     * register a CheckBox Group
21018     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21019     */
21020     register : function(checkbox)
21021     {
21022         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21023             this.groups[checkbox.groupId] = {};
21024         }
21025         
21026         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21027             return;
21028         }
21029         
21030         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21031         
21032     },
21033     /**
21034     * fetch a CheckBox Group based on the group ID
21035     * @param {string} the group ID
21036     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21037     */
21038     get: function(groupId) {
21039         if (typeof(this.groups[groupId]) == 'undefined') {
21040             return false;
21041         }
21042         
21043         return this.groups[groupId] ;
21044     }
21045     
21046     
21047 });
21048 /*
21049  * - LGPL
21050  *
21051  * RadioItem
21052  * 
21053  */
21054
21055 /**
21056  * @class Roo.bootstrap.Radio
21057  * @extends Roo.bootstrap.Component
21058  * Bootstrap Radio class
21059  * @cfg {String} boxLabel - the label associated
21060  * @cfg {String} value - the value of radio
21061  * 
21062  * @constructor
21063  * Create a new Radio
21064  * @param {Object} config The config object
21065  */
21066 Roo.bootstrap.Radio = function(config){
21067     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21068     
21069 };
21070
21071 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21072     
21073     boxLabel : '',
21074     
21075     value : '',
21076     
21077     getAutoCreate : function()
21078     {
21079         var cfg = {
21080             tag : 'div',
21081             cls : 'form-group radio',
21082             cn : [
21083                 {
21084                     tag : 'label',
21085                     cls : 'box-label',
21086                     html : this.boxLabel
21087                 }
21088             ]
21089         };
21090         
21091         return cfg;
21092     },
21093     
21094     initEvents : function() 
21095     {
21096         this.parent().register(this);
21097         
21098         this.el.on('click', this.onClick, this);
21099         
21100     },
21101     
21102     onClick : function(e)
21103     {
21104         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21105             this.setChecked(true);
21106         }
21107     },
21108     
21109     setChecked : function(state, suppressEvent)
21110     {
21111         this.parent().setValue(this.value, suppressEvent);
21112         
21113     },
21114     
21115     setBoxLabel : function(v)
21116     {
21117         this.boxLabel = v;
21118         
21119         if(this.rendered){
21120             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21121         }
21122     }
21123     
21124 });
21125  
21126
21127  /*
21128  * - LGPL
21129  *
21130  * Input
21131  * 
21132  */
21133
21134 /**
21135  * @class Roo.bootstrap.SecurePass
21136  * @extends Roo.bootstrap.Input
21137  * Bootstrap SecurePass class
21138  *
21139  * 
21140  * @constructor
21141  * Create a new SecurePass
21142  * @param {Object} config The config object
21143  */
21144  
21145 Roo.bootstrap.SecurePass = function (config) {
21146     // these go here, so the translation tool can replace them..
21147     this.errors = {
21148         PwdEmpty: "Please type a password, and then retype it to confirm.",
21149         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21150         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21151         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21152         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21153         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21154         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21155         TooWeak: "Your password is Too Weak."
21156     },
21157     this.meterLabel = "Password strength:";
21158     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21159     this.meterClass = [
21160         "roo-password-meter-tooweak", 
21161         "roo-password-meter-weak", 
21162         "roo-password-meter-medium", 
21163         "roo-password-meter-strong", 
21164         "roo-password-meter-grey"
21165     ];
21166     
21167     this.errors = {};
21168     
21169     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21170 }
21171
21172 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21173     /**
21174      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21175      * {
21176      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21177      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21178      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21179      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21180      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21181      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21182      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21183      * })
21184      */
21185     // private
21186     
21187     meterWidth: 300,
21188     errorMsg :'',    
21189     errors: false,
21190     imageRoot: '/',
21191     /**
21192      * @cfg {String/Object} Label for the strength meter (defaults to
21193      * 'Password strength:')
21194      */
21195     // private
21196     meterLabel: '',
21197     /**
21198      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21199      * ['Weak', 'Medium', 'Strong'])
21200      */
21201     // private    
21202     pwdStrengths: false,    
21203     // private
21204     strength: 0,
21205     // private
21206     _lastPwd: null,
21207     // private
21208     kCapitalLetter: 0,
21209     kSmallLetter: 1,
21210     kDigit: 2,
21211     kPunctuation: 3,
21212     
21213     insecure: false,
21214     // private
21215     initEvents: function ()
21216     {
21217         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21218
21219         if (this.el.is('input[type=password]') && Roo.isSafari) {
21220             this.el.on('keydown', this.SafariOnKeyDown, this);
21221         }
21222
21223         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21224     },
21225     // private
21226     onRender: function (ct, position)
21227     {
21228         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21229         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21230         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21231
21232         this.trigger.createChild({
21233                    cn: [
21234                     {
21235                     //id: 'PwdMeter',
21236                     tag: 'div',
21237                     cls: 'roo-password-meter-grey col-xs-12',
21238                     style: {
21239                         //width: 0,
21240                         //width: this.meterWidth + 'px'                                                
21241                         }
21242                     },
21243                     {                            
21244                          cls: 'roo-password-meter-text'                          
21245                     }
21246                 ]            
21247         });
21248
21249          
21250         if (this.hideTrigger) {
21251             this.trigger.setDisplayed(false);
21252         }
21253         this.setSize(this.width || '', this.height || '');
21254     },
21255     // private
21256     onDestroy: function ()
21257     {
21258         if (this.trigger) {
21259             this.trigger.removeAllListeners();
21260             this.trigger.remove();
21261         }
21262         if (this.wrap) {
21263             this.wrap.remove();
21264         }
21265         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21266     },
21267     // private
21268     checkStrength: function ()
21269     {
21270         var pwd = this.inputEl().getValue();
21271         if (pwd == this._lastPwd) {
21272             return;
21273         }
21274
21275         var strength;
21276         if (this.ClientSideStrongPassword(pwd)) {
21277             strength = 3;
21278         } else if (this.ClientSideMediumPassword(pwd)) {
21279             strength = 2;
21280         } else if (this.ClientSideWeakPassword(pwd)) {
21281             strength = 1;
21282         } else {
21283             strength = 0;
21284         }
21285         
21286         Roo.log('strength1: ' + strength);
21287         
21288         //var pm = this.trigger.child('div/div/div').dom;
21289         var pm = this.trigger.child('div/div');
21290         pm.removeClass(this.meterClass);
21291         pm.addClass(this.meterClass[strength]);
21292                 
21293         
21294         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21295                 
21296         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21297         
21298         this._lastPwd = pwd;
21299     },
21300     reset: function ()
21301     {
21302         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21303         
21304         this._lastPwd = '';
21305         
21306         var pm = this.trigger.child('div/div');
21307         pm.removeClass(this.meterClass);
21308         pm.addClass('roo-password-meter-grey');        
21309         
21310         
21311         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21312         
21313         pt.innerHTML = '';
21314         this.inputEl().dom.type='password';
21315     },
21316     // private
21317     validateValue: function (value)
21318     {
21319         
21320         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21321             return false;
21322         }
21323         if (value.length == 0) {
21324             if (this.allowBlank) {
21325                 this.clearInvalid();
21326                 return true;
21327             }
21328
21329             this.markInvalid(this.errors.PwdEmpty);
21330             this.errorMsg = this.errors.PwdEmpty;
21331             return false;
21332         }
21333         
21334         if(this.insecure){
21335             return true;
21336         }
21337         
21338         if ('[\x21-\x7e]*'.match(value)) {
21339             this.markInvalid(this.errors.PwdBadChar);
21340             this.errorMsg = this.errors.PwdBadChar;
21341             return false;
21342         }
21343         if (value.length < 6) {
21344             this.markInvalid(this.errors.PwdShort);
21345             this.errorMsg = this.errors.PwdShort;
21346             return false;
21347         }
21348         if (value.length > 16) {
21349             this.markInvalid(this.errors.PwdLong);
21350             this.errorMsg = this.errors.PwdLong;
21351             return false;
21352         }
21353         var strength;
21354         if (this.ClientSideStrongPassword(value)) {
21355             strength = 3;
21356         } else if (this.ClientSideMediumPassword(value)) {
21357             strength = 2;
21358         } else if (this.ClientSideWeakPassword(value)) {
21359             strength = 1;
21360         } else {
21361             strength = 0;
21362         }
21363
21364         
21365         if (strength < 2) {
21366             //this.markInvalid(this.errors.TooWeak);
21367             this.errorMsg = this.errors.TooWeak;
21368             //return false;
21369         }
21370         
21371         
21372         console.log('strength2: ' + strength);
21373         
21374         //var pm = this.trigger.child('div/div/div').dom;
21375         
21376         var pm = this.trigger.child('div/div');
21377         pm.removeClass(this.meterClass);
21378         pm.addClass(this.meterClass[strength]);
21379                 
21380         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21381                 
21382         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21383         
21384         this.errorMsg = ''; 
21385         return true;
21386     },
21387     // private
21388     CharacterSetChecks: function (type)
21389     {
21390         this.type = type;
21391         this.fResult = false;
21392     },
21393     // private
21394     isctype: function (character, type)
21395     {
21396         switch (type) {  
21397             case this.kCapitalLetter:
21398                 if (character >= 'A' && character <= 'Z') {
21399                     return true;
21400                 }
21401                 break;
21402             
21403             case this.kSmallLetter:
21404                 if (character >= 'a' && character <= 'z') {
21405                     return true;
21406                 }
21407                 break;
21408             
21409             case this.kDigit:
21410                 if (character >= '0' && character <= '9') {
21411                     return true;
21412                 }
21413                 break;
21414             
21415             case this.kPunctuation:
21416                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21417                     return true;
21418                 }
21419                 break;
21420             
21421             default:
21422                 return false;
21423         }
21424
21425     },
21426     // private
21427     IsLongEnough: function (pwd, size)
21428     {
21429         return !(pwd == null || isNaN(size) || pwd.length < size);
21430     },
21431     // private
21432     SpansEnoughCharacterSets: function (word, nb)
21433     {
21434         if (!this.IsLongEnough(word, nb))
21435         {
21436             return false;
21437         }
21438
21439         var characterSetChecks = new Array(
21440             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21441             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21442         );
21443         
21444         for (var index = 0; index < word.length; ++index) {
21445             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21446                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21447                     characterSetChecks[nCharSet].fResult = true;
21448                     break;
21449                 }
21450             }
21451         }
21452
21453         var nCharSets = 0;
21454         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21455             if (characterSetChecks[nCharSet].fResult) {
21456                 ++nCharSets;
21457             }
21458         }
21459
21460         if (nCharSets < nb) {
21461             return false;
21462         }
21463         return true;
21464     },
21465     // private
21466     ClientSideStrongPassword: function (pwd)
21467     {
21468         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21469     },
21470     // private
21471     ClientSideMediumPassword: function (pwd)
21472     {
21473         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21474     },
21475     // private
21476     ClientSideWeakPassword: function (pwd)
21477     {
21478         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21479     }
21480           
21481 })//<script type="text/javascript">
21482
21483 /*
21484  * Based  Ext JS Library 1.1.1
21485  * Copyright(c) 2006-2007, Ext JS, LLC.
21486  * LGPL
21487  *
21488  */
21489  
21490 /**
21491  * @class Roo.HtmlEditorCore
21492  * @extends Roo.Component
21493  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21494  *
21495  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21496  */
21497
21498 Roo.HtmlEditorCore = function(config){
21499     
21500     
21501     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21502     
21503     
21504     this.addEvents({
21505         /**
21506          * @event initialize
21507          * Fires when the editor is fully initialized (including the iframe)
21508          * @param {Roo.HtmlEditorCore} this
21509          */
21510         initialize: true,
21511         /**
21512          * @event activate
21513          * Fires when the editor is first receives the focus. Any insertion must wait
21514          * until after this event.
21515          * @param {Roo.HtmlEditorCore} this
21516          */
21517         activate: true,
21518          /**
21519          * @event beforesync
21520          * Fires before the textarea is updated with content from the editor iframe. Return false
21521          * to cancel the sync.
21522          * @param {Roo.HtmlEditorCore} this
21523          * @param {String} html
21524          */
21525         beforesync: true,
21526          /**
21527          * @event beforepush
21528          * Fires before the iframe editor is updated with content from the textarea. Return false
21529          * to cancel the push.
21530          * @param {Roo.HtmlEditorCore} this
21531          * @param {String} html
21532          */
21533         beforepush: true,
21534          /**
21535          * @event sync
21536          * Fires when the textarea is updated with content from the editor iframe.
21537          * @param {Roo.HtmlEditorCore} this
21538          * @param {String} html
21539          */
21540         sync: true,
21541          /**
21542          * @event push
21543          * Fires when the iframe editor is updated with content from the textarea.
21544          * @param {Roo.HtmlEditorCore} this
21545          * @param {String} html
21546          */
21547         push: true,
21548         
21549         /**
21550          * @event editorevent
21551          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21552          * @param {Roo.HtmlEditorCore} this
21553          */
21554         editorevent: true
21555         
21556     });
21557     
21558     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21559     
21560     // defaults : white / black...
21561     this.applyBlacklists();
21562     
21563     
21564     
21565 };
21566
21567
21568 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21569
21570
21571      /**
21572      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21573      */
21574     
21575     owner : false,
21576     
21577      /**
21578      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21579      *                        Roo.resizable.
21580      */
21581     resizable : false,
21582      /**
21583      * @cfg {Number} height (in pixels)
21584      */   
21585     height: 300,
21586    /**
21587      * @cfg {Number} width (in pixels)
21588      */   
21589     width: 500,
21590     
21591     /**
21592      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21593      * 
21594      */
21595     stylesheets: false,
21596     
21597     // id of frame..
21598     frameId: false,
21599     
21600     // private properties
21601     validationEvent : false,
21602     deferHeight: true,
21603     initialized : false,
21604     activated : false,
21605     sourceEditMode : false,
21606     onFocus : Roo.emptyFn,
21607     iframePad:3,
21608     hideMode:'offsets',
21609     
21610     clearUp: true,
21611     
21612     // blacklist + whitelisted elements..
21613     black: false,
21614     white: false,
21615      
21616     bodyCls : '',
21617
21618     /**
21619      * Protected method that will not generally be called directly. It
21620      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21621      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21622      */
21623     getDocMarkup : function(){
21624         // body styles..
21625         var st = '';
21626         
21627         // inherit styels from page...?? 
21628         if (this.stylesheets === false) {
21629             
21630             Roo.get(document.head).select('style').each(function(node) {
21631                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21632             });
21633             
21634             Roo.get(document.head).select('link').each(function(node) { 
21635                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21636             });
21637             
21638         } else if (!this.stylesheets.length) {
21639                 // simple..
21640                 st = '<style type="text/css">' +
21641                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21642                    '</style>';
21643         } else { 
21644             st = '<style type="text/css">' +
21645                     this.stylesheets +
21646                 '</style>';
21647         }
21648         
21649         st +=  '<style type="text/css">' +
21650             'IMG { cursor: pointer } ' +
21651         '</style>';
21652
21653         var cls = 'roo-htmleditor-body';
21654         
21655         if(this.bodyCls.length){
21656             cls += ' ' + this.bodyCls;
21657         }
21658         
21659         return '<html><head>' + st  +
21660             //<style type="text/css">' +
21661             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21662             //'</style>' +
21663             ' </head><body class="' +  cls + '"></body></html>';
21664     },
21665
21666     // private
21667     onRender : function(ct, position)
21668     {
21669         var _t = this;
21670         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21671         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21672         
21673         
21674         this.el.dom.style.border = '0 none';
21675         this.el.dom.setAttribute('tabIndex', -1);
21676         this.el.addClass('x-hidden hide');
21677         
21678         
21679         
21680         if(Roo.isIE){ // fix IE 1px bogus margin
21681             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21682         }
21683        
21684         
21685         this.frameId = Roo.id();
21686         
21687          
21688         
21689         var iframe = this.owner.wrap.createChild({
21690             tag: 'iframe',
21691             cls: 'form-control', // bootstrap..
21692             id: this.frameId,
21693             name: this.frameId,
21694             frameBorder : 'no',
21695             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21696         }, this.el
21697         );
21698         
21699         
21700         this.iframe = iframe.dom;
21701
21702          this.assignDocWin();
21703         
21704         this.doc.designMode = 'on';
21705        
21706         this.doc.open();
21707         this.doc.write(this.getDocMarkup());
21708         this.doc.close();
21709
21710         
21711         var task = { // must defer to wait for browser to be ready
21712             run : function(){
21713                 //console.log("run task?" + this.doc.readyState);
21714                 this.assignDocWin();
21715                 if(this.doc.body || this.doc.readyState == 'complete'){
21716                     try {
21717                         this.doc.designMode="on";
21718                     } catch (e) {
21719                         return;
21720                     }
21721                     Roo.TaskMgr.stop(task);
21722                     this.initEditor.defer(10, this);
21723                 }
21724             },
21725             interval : 10,
21726             duration: 10000,
21727             scope: this
21728         };
21729         Roo.TaskMgr.start(task);
21730
21731     },
21732
21733     // private
21734     onResize : function(w, h)
21735     {
21736          Roo.log('resize: ' +w + ',' + h );
21737         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21738         if(!this.iframe){
21739             return;
21740         }
21741         if(typeof w == 'number'){
21742             
21743             this.iframe.style.width = w + 'px';
21744         }
21745         if(typeof h == 'number'){
21746             
21747             this.iframe.style.height = h + 'px';
21748             if(this.doc){
21749                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21750             }
21751         }
21752         
21753     },
21754
21755     /**
21756      * Toggles the editor between standard and source edit mode.
21757      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21758      */
21759     toggleSourceEdit : function(sourceEditMode){
21760         
21761         this.sourceEditMode = sourceEditMode === true;
21762         
21763         if(this.sourceEditMode){
21764  
21765             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21766             
21767         }else{
21768             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21769             //this.iframe.className = '';
21770             this.deferFocus();
21771         }
21772         //this.setSize(this.owner.wrap.getSize());
21773         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21774     },
21775
21776     
21777   
21778
21779     /**
21780      * Protected method that will not generally be called directly. If you need/want
21781      * custom HTML cleanup, this is the method you should override.
21782      * @param {String} html The HTML to be cleaned
21783      * return {String} The cleaned HTML
21784      */
21785     cleanHtml : function(html){
21786         html = String(html);
21787         if(html.length > 5){
21788             if(Roo.isSafari){ // strip safari nonsense
21789                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21790             }
21791         }
21792         if(html == '&nbsp;'){
21793             html = '';
21794         }
21795         return html;
21796     },
21797
21798     /**
21799      * HTML Editor -> Textarea
21800      * Protected method that will not generally be called directly. Syncs the contents
21801      * of the editor iframe with the textarea.
21802      */
21803     syncValue : function(){
21804         if(this.initialized){
21805             var bd = (this.doc.body || this.doc.documentElement);
21806             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21807             var html = bd.innerHTML;
21808             if(Roo.isSafari){
21809                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21810                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21811                 if(m && m[1]){
21812                     html = '<div style="'+m[0]+'">' + html + '</div>';
21813                 }
21814             }
21815             html = this.cleanHtml(html);
21816             // fix up the special chars.. normaly like back quotes in word...
21817             // however we do not want to do this with chinese..
21818             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21819                 var cc = b.charCodeAt();
21820                 if (
21821                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21822                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21823                     (cc >= 0xf900 && cc < 0xfb00 )
21824                 ) {
21825                         return b;
21826                 }
21827                 return "&#"+cc+";" 
21828             });
21829             if(this.owner.fireEvent('beforesync', this, html) !== false){
21830                 this.el.dom.value = html;
21831                 this.owner.fireEvent('sync', this, html);
21832             }
21833         }
21834     },
21835
21836     /**
21837      * Protected method that will not generally be called directly. Pushes the value of the textarea
21838      * into the iframe editor.
21839      */
21840     pushValue : function(){
21841         if(this.initialized){
21842             var v = this.el.dom.value.trim();
21843             
21844 //            if(v.length < 1){
21845 //                v = '&#160;';
21846 //            }
21847             
21848             if(this.owner.fireEvent('beforepush', this, v) !== false){
21849                 var d = (this.doc.body || this.doc.documentElement);
21850                 d.innerHTML = v;
21851                 this.cleanUpPaste();
21852                 this.el.dom.value = d.innerHTML;
21853                 this.owner.fireEvent('push', this, v);
21854             }
21855         }
21856     },
21857
21858     // private
21859     deferFocus : function(){
21860         this.focus.defer(10, this);
21861     },
21862
21863     // doc'ed in Field
21864     focus : function(){
21865         if(this.win && !this.sourceEditMode){
21866             this.win.focus();
21867         }else{
21868             this.el.focus();
21869         }
21870     },
21871     
21872     assignDocWin: function()
21873     {
21874         var iframe = this.iframe;
21875         
21876          if(Roo.isIE){
21877             this.doc = iframe.contentWindow.document;
21878             this.win = iframe.contentWindow;
21879         } else {
21880 //            if (!Roo.get(this.frameId)) {
21881 //                return;
21882 //            }
21883 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21884 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21885             
21886             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21887                 return;
21888             }
21889             
21890             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21891             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21892         }
21893     },
21894     
21895     // private
21896     initEditor : function(){
21897         //console.log("INIT EDITOR");
21898         this.assignDocWin();
21899         
21900         
21901         
21902         this.doc.designMode="on";
21903         this.doc.open();
21904         this.doc.write(this.getDocMarkup());
21905         this.doc.close();
21906         
21907         var dbody = (this.doc.body || this.doc.documentElement);
21908         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21909         // this copies styles from the containing element into thsi one..
21910         // not sure why we need all of this..
21911         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21912         
21913         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21914         //ss['background-attachment'] = 'fixed'; // w3c
21915         dbody.bgProperties = 'fixed'; // ie
21916         //Roo.DomHelper.applyStyles(dbody, ss);
21917         Roo.EventManager.on(this.doc, {
21918             //'mousedown': this.onEditorEvent,
21919             'mouseup': this.onEditorEvent,
21920             'dblclick': this.onEditorEvent,
21921             'click': this.onEditorEvent,
21922             'keyup': this.onEditorEvent,
21923             buffer:100,
21924             scope: this
21925         });
21926         if(Roo.isGecko){
21927             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21928         }
21929         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21930             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21931         }
21932         this.initialized = true;
21933
21934         this.owner.fireEvent('initialize', this);
21935         this.pushValue();
21936     },
21937
21938     // private
21939     onDestroy : function(){
21940         
21941         
21942         
21943         if(this.rendered){
21944             
21945             //for (var i =0; i < this.toolbars.length;i++) {
21946             //    // fixme - ask toolbars for heights?
21947             //    this.toolbars[i].onDestroy();
21948            // }
21949             
21950             //this.wrap.dom.innerHTML = '';
21951             //this.wrap.remove();
21952         }
21953     },
21954
21955     // private
21956     onFirstFocus : function(){
21957         
21958         this.assignDocWin();
21959         
21960         
21961         this.activated = true;
21962          
21963     
21964         if(Roo.isGecko){ // prevent silly gecko errors
21965             this.win.focus();
21966             var s = this.win.getSelection();
21967             if(!s.focusNode || s.focusNode.nodeType != 3){
21968                 var r = s.getRangeAt(0);
21969                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21970                 r.collapse(true);
21971                 this.deferFocus();
21972             }
21973             try{
21974                 this.execCmd('useCSS', true);
21975                 this.execCmd('styleWithCSS', false);
21976             }catch(e){}
21977         }
21978         this.owner.fireEvent('activate', this);
21979     },
21980
21981     // private
21982     adjustFont: function(btn){
21983         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21984         //if(Roo.isSafari){ // safari
21985         //    adjust *= 2;
21986        // }
21987         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21988         if(Roo.isSafari){ // safari
21989             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21990             v =  (v < 10) ? 10 : v;
21991             v =  (v > 48) ? 48 : v;
21992             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21993             
21994         }
21995         
21996         
21997         v = Math.max(1, v+adjust);
21998         
21999         this.execCmd('FontSize', v  );
22000     },
22001
22002     onEditorEvent : function(e)
22003     {
22004         this.owner.fireEvent('editorevent', this, e);
22005       //  this.updateToolbar();
22006         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22007     },
22008
22009     insertTag : function(tg)
22010     {
22011         // could be a bit smarter... -> wrap the current selected tRoo..
22012         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22013             
22014             range = this.createRange(this.getSelection());
22015             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22016             wrappingNode.appendChild(range.extractContents());
22017             range.insertNode(wrappingNode);
22018
22019             return;
22020             
22021             
22022             
22023         }
22024         this.execCmd("formatblock",   tg);
22025         
22026     },
22027     
22028     insertText : function(txt)
22029     {
22030         
22031         
22032         var range = this.createRange();
22033         range.deleteContents();
22034                //alert(Sender.getAttribute('label'));
22035                
22036         range.insertNode(this.doc.createTextNode(txt));
22037     } ,
22038     
22039      
22040
22041     /**
22042      * Executes a Midas editor command on the editor document and performs necessary focus and
22043      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22044      * @param {String} cmd The Midas command
22045      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22046      */
22047     relayCmd : function(cmd, value){
22048         this.win.focus();
22049         this.execCmd(cmd, value);
22050         this.owner.fireEvent('editorevent', this);
22051         //this.updateToolbar();
22052         this.owner.deferFocus();
22053     },
22054
22055     /**
22056      * Executes a Midas editor command directly on the editor document.
22057      * For visual commands, you should use {@link #relayCmd} instead.
22058      * <b>This should only be called after the editor is initialized.</b>
22059      * @param {String} cmd The Midas command
22060      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22061      */
22062     execCmd : function(cmd, value){
22063         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22064         this.syncValue();
22065     },
22066  
22067  
22068    
22069     /**
22070      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22071      * to insert tRoo.
22072      * @param {String} text | dom node.. 
22073      */
22074     insertAtCursor : function(text)
22075     {
22076         
22077         if(!this.activated){
22078             return;
22079         }
22080         /*
22081         if(Roo.isIE){
22082             this.win.focus();
22083             var r = this.doc.selection.createRange();
22084             if(r){
22085                 r.collapse(true);
22086                 r.pasteHTML(text);
22087                 this.syncValue();
22088                 this.deferFocus();
22089             
22090             }
22091             return;
22092         }
22093         */
22094         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22095             this.win.focus();
22096             
22097             
22098             // from jquery ui (MIT licenced)
22099             var range, node;
22100             var win = this.win;
22101             
22102             if (win.getSelection && win.getSelection().getRangeAt) {
22103                 range = win.getSelection().getRangeAt(0);
22104                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22105                 range.insertNode(node);
22106             } else if (win.document.selection && win.document.selection.createRange) {
22107                 // no firefox support
22108                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22109                 win.document.selection.createRange().pasteHTML(txt);
22110             } else {
22111                 // no firefox support
22112                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22113                 this.execCmd('InsertHTML', txt);
22114             } 
22115             
22116             this.syncValue();
22117             
22118             this.deferFocus();
22119         }
22120     },
22121  // private
22122     mozKeyPress : function(e){
22123         if(e.ctrlKey){
22124             var c = e.getCharCode(), cmd;
22125           
22126             if(c > 0){
22127                 c = String.fromCharCode(c).toLowerCase();
22128                 switch(c){
22129                     case 'b':
22130                         cmd = 'bold';
22131                         break;
22132                     case 'i':
22133                         cmd = 'italic';
22134                         break;
22135                     
22136                     case 'u':
22137                         cmd = 'underline';
22138                         break;
22139                     
22140                     case 'v':
22141                         this.cleanUpPaste.defer(100, this);
22142                         return;
22143                         
22144                 }
22145                 if(cmd){
22146                     this.win.focus();
22147                     this.execCmd(cmd);
22148                     this.deferFocus();
22149                     e.preventDefault();
22150                 }
22151                 
22152             }
22153         }
22154     },
22155
22156     // private
22157     fixKeys : function(){ // load time branching for fastest keydown performance
22158         if(Roo.isIE){
22159             return function(e){
22160                 var k = e.getKey(), r;
22161                 if(k == e.TAB){
22162                     e.stopEvent();
22163                     r = this.doc.selection.createRange();
22164                     if(r){
22165                         r.collapse(true);
22166                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22167                         this.deferFocus();
22168                     }
22169                     return;
22170                 }
22171                 
22172                 if(k == e.ENTER){
22173                     r = this.doc.selection.createRange();
22174                     if(r){
22175                         var target = r.parentElement();
22176                         if(!target || target.tagName.toLowerCase() != 'li'){
22177                             e.stopEvent();
22178                             r.pasteHTML('<br />');
22179                             r.collapse(false);
22180                             r.select();
22181                         }
22182                     }
22183                 }
22184                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22185                     this.cleanUpPaste.defer(100, this);
22186                     return;
22187                 }
22188                 
22189                 
22190             };
22191         }else if(Roo.isOpera){
22192             return function(e){
22193                 var k = e.getKey();
22194                 if(k == e.TAB){
22195                     e.stopEvent();
22196                     this.win.focus();
22197                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22198                     this.deferFocus();
22199                 }
22200                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22201                     this.cleanUpPaste.defer(100, this);
22202                     return;
22203                 }
22204                 
22205             };
22206         }else if(Roo.isSafari){
22207             return function(e){
22208                 var k = e.getKey();
22209                 
22210                 if(k == e.TAB){
22211                     e.stopEvent();
22212                     this.execCmd('InsertText','\t');
22213                     this.deferFocus();
22214                     return;
22215                 }
22216                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22217                     this.cleanUpPaste.defer(100, this);
22218                     return;
22219                 }
22220                 
22221              };
22222         }
22223     }(),
22224     
22225     getAllAncestors: function()
22226     {
22227         var p = this.getSelectedNode();
22228         var a = [];
22229         if (!p) {
22230             a.push(p); // push blank onto stack..
22231             p = this.getParentElement();
22232         }
22233         
22234         
22235         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22236             a.push(p);
22237             p = p.parentNode;
22238         }
22239         a.push(this.doc.body);
22240         return a;
22241     },
22242     lastSel : false,
22243     lastSelNode : false,
22244     
22245     
22246     getSelection : function() 
22247     {
22248         this.assignDocWin();
22249         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22250     },
22251     
22252     getSelectedNode: function() 
22253     {
22254         // this may only work on Gecko!!!
22255         
22256         // should we cache this!!!!
22257         
22258         
22259         
22260          
22261         var range = this.createRange(this.getSelection()).cloneRange();
22262         
22263         if (Roo.isIE) {
22264             var parent = range.parentElement();
22265             while (true) {
22266                 var testRange = range.duplicate();
22267                 testRange.moveToElementText(parent);
22268                 if (testRange.inRange(range)) {
22269                     break;
22270                 }
22271                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22272                     break;
22273                 }
22274                 parent = parent.parentElement;
22275             }
22276             return parent;
22277         }
22278         
22279         // is ancestor a text element.
22280         var ac =  range.commonAncestorContainer;
22281         if (ac.nodeType == 3) {
22282             ac = ac.parentNode;
22283         }
22284         
22285         var ar = ac.childNodes;
22286          
22287         var nodes = [];
22288         var other_nodes = [];
22289         var has_other_nodes = false;
22290         for (var i=0;i<ar.length;i++) {
22291             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22292                 continue;
22293             }
22294             // fullly contained node.
22295             
22296             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22297                 nodes.push(ar[i]);
22298                 continue;
22299             }
22300             
22301             // probably selected..
22302             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22303                 other_nodes.push(ar[i]);
22304                 continue;
22305             }
22306             // outer..
22307             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22308                 continue;
22309             }
22310             
22311             
22312             has_other_nodes = true;
22313         }
22314         if (!nodes.length && other_nodes.length) {
22315             nodes= other_nodes;
22316         }
22317         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22318             return false;
22319         }
22320         
22321         return nodes[0];
22322     },
22323     createRange: function(sel)
22324     {
22325         // this has strange effects when using with 
22326         // top toolbar - not sure if it's a great idea.
22327         //this.editor.contentWindow.focus();
22328         if (typeof sel != "undefined") {
22329             try {
22330                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22331             } catch(e) {
22332                 return this.doc.createRange();
22333             }
22334         } else {
22335             return this.doc.createRange();
22336         }
22337     },
22338     getParentElement: function()
22339     {
22340         
22341         this.assignDocWin();
22342         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22343         
22344         var range = this.createRange(sel);
22345          
22346         try {
22347             var p = range.commonAncestorContainer;
22348             while (p.nodeType == 3) { // text node
22349                 p = p.parentNode;
22350             }
22351             return p;
22352         } catch (e) {
22353             return null;
22354         }
22355     
22356     },
22357     /***
22358      *
22359      * Range intersection.. the hard stuff...
22360      *  '-1' = before
22361      *  '0' = hits..
22362      *  '1' = after.
22363      *         [ -- selected range --- ]
22364      *   [fail]                        [fail]
22365      *
22366      *    basically..
22367      *      if end is before start or  hits it. fail.
22368      *      if start is after end or hits it fail.
22369      *
22370      *   if either hits (but other is outside. - then it's not 
22371      *   
22372      *    
22373      **/
22374     
22375     
22376     // @see http://www.thismuchiknow.co.uk/?p=64.
22377     rangeIntersectsNode : function(range, node)
22378     {
22379         var nodeRange = node.ownerDocument.createRange();
22380         try {
22381             nodeRange.selectNode(node);
22382         } catch (e) {
22383             nodeRange.selectNodeContents(node);
22384         }
22385     
22386         var rangeStartRange = range.cloneRange();
22387         rangeStartRange.collapse(true);
22388     
22389         var rangeEndRange = range.cloneRange();
22390         rangeEndRange.collapse(false);
22391     
22392         var nodeStartRange = nodeRange.cloneRange();
22393         nodeStartRange.collapse(true);
22394     
22395         var nodeEndRange = nodeRange.cloneRange();
22396         nodeEndRange.collapse(false);
22397     
22398         return rangeStartRange.compareBoundaryPoints(
22399                  Range.START_TO_START, nodeEndRange) == -1 &&
22400                rangeEndRange.compareBoundaryPoints(
22401                  Range.START_TO_START, nodeStartRange) == 1;
22402         
22403          
22404     },
22405     rangeCompareNode : function(range, node)
22406     {
22407         var nodeRange = node.ownerDocument.createRange();
22408         try {
22409             nodeRange.selectNode(node);
22410         } catch (e) {
22411             nodeRange.selectNodeContents(node);
22412         }
22413         
22414         
22415         range.collapse(true);
22416     
22417         nodeRange.collapse(true);
22418      
22419         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22420         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22421          
22422         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22423         
22424         var nodeIsBefore   =  ss == 1;
22425         var nodeIsAfter    = ee == -1;
22426         
22427         if (nodeIsBefore && nodeIsAfter) {
22428             return 0; // outer
22429         }
22430         if (!nodeIsBefore && nodeIsAfter) {
22431             return 1; //right trailed.
22432         }
22433         
22434         if (nodeIsBefore && !nodeIsAfter) {
22435             return 2;  // left trailed.
22436         }
22437         // fully contined.
22438         return 3;
22439     },
22440
22441     // private? - in a new class?
22442     cleanUpPaste :  function()
22443     {
22444         // cleans up the whole document..
22445         Roo.log('cleanuppaste');
22446         
22447         this.cleanUpChildren(this.doc.body);
22448         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22449         if (clean != this.doc.body.innerHTML) {
22450             this.doc.body.innerHTML = clean;
22451         }
22452         
22453     },
22454     
22455     cleanWordChars : function(input) {// change the chars to hex code
22456         var he = Roo.HtmlEditorCore;
22457         
22458         var output = input;
22459         Roo.each(he.swapCodes, function(sw) { 
22460             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22461             
22462             output = output.replace(swapper, sw[1]);
22463         });
22464         
22465         return output;
22466     },
22467     
22468     
22469     cleanUpChildren : function (n)
22470     {
22471         if (!n.childNodes.length) {
22472             return;
22473         }
22474         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22475            this.cleanUpChild(n.childNodes[i]);
22476         }
22477     },
22478     
22479     
22480         
22481     
22482     cleanUpChild : function (node)
22483     {
22484         var ed = this;
22485         //console.log(node);
22486         if (node.nodeName == "#text") {
22487             // clean up silly Windows -- stuff?
22488             return; 
22489         }
22490         if (node.nodeName == "#comment") {
22491             node.parentNode.removeChild(node);
22492             // clean up silly Windows -- stuff?
22493             return; 
22494         }
22495         var lcname = node.tagName.toLowerCase();
22496         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22497         // whitelist of tags..
22498         
22499         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22500             // remove node.
22501             node.parentNode.removeChild(node);
22502             return;
22503             
22504         }
22505         
22506         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22507         
22508         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22509         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22510         
22511         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22512         //    remove_keep_children = true;
22513         //}
22514         
22515         if (remove_keep_children) {
22516             this.cleanUpChildren(node);
22517             // inserts everything just before this node...
22518             while (node.childNodes.length) {
22519                 var cn = node.childNodes[0];
22520                 node.removeChild(cn);
22521                 node.parentNode.insertBefore(cn, node);
22522             }
22523             node.parentNode.removeChild(node);
22524             return;
22525         }
22526         
22527         if (!node.attributes || !node.attributes.length) {
22528             this.cleanUpChildren(node);
22529             return;
22530         }
22531         
22532         function cleanAttr(n,v)
22533         {
22534             
22535             if (v.match(/^\./) || v.match(/^\//)) {
22536                 return;
22537             }
22538             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22539                 return;
22540             }
22541             if (v.match(/^#/)) {
22542                 return;
22543             }
22544 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22545             node.removeAttribute(n);
22546             
22547         }
22548         
22549         var cwhite = this.cwhite;
22550         var cblack = this.cblack;
22551             
22552         function cleanStyle(n,v)
22553         {
22554             if (v.match(/expression/)) { //XSS?? should we even bother..
22555                 node.removeAttribute(n);
22556                 return;
22557             }
22558             
22559             var parts = v.split(/;/);
22560             var clean = [];
22561             
22562             Roo.each(parts, function(p) {
22563                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22564                 if (!p.length) {
22565                     return true;
22566                 }
22567                 var l = p.split(':').shift().replace(/\s+/g,'');
22568                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22569                 
22570                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22571 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22572                     //node.removeAttribute(n);
22573                     return true;
22574                 }
22575                 //Roo.log()
22576                 // only allow 'c whitelisted system attributes'
22577                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22578 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22579                     //node.removeAttribute(n);
22580                     return true;
22581                 }
22582                 
22583                 
22584                  
22585                 
22586                 clean.push(p);
22587                 return true;
22588             });
22589             if (clean.length) { 
22590                 node.setAttribute(n, clean.join(';'));
22591             } else {
22592                 node.removeAttribute(n);
22593             }
22594             
22595         }
22596         
22597         
22598         for (var i = node.attributes.length-1; i > -1 ; i--) {
22599             var a = node.attributes[i];
22600             //console.log(a);
22601             
22602             if (a.name.toLowerCase().substr(0,2)=='on')  {
22603                 node.removeAttribute(a.name);
22604                 continue;
22605             }
22606             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22607                 node.removeAttribute(a.name);
22608                 continue;
22609             }
22610             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22611                 cleanAttr(a.name,a.value); // fixme..
22612                 continue;
22613             }
22614             if (a.name == 'style') {
22615                 cleanStyle(a.name,a.value);
22616                 continue;
22617             }
22618             /// clean up MS crap..
22619             // tecnically this should be a list of valid class'es..
22620             
22621             
22622             if (a.name == 'class') {
22623                 if (a.value.match(/^Mso/)) {
22624                     node.className = '';
22625                 }
22626                 
22627                 if (a.value.match(/^body$/)) {
22628                     node.className = '';
22629                 }
22630                 continue;
22631             }
22632             
22633             // style cleanup!?
22634             // class cleanup?
22635             
22636         }
22637         
22638         
22639         this.cleanUpChildren(node);
22640         
22641         
22642     },
22643     
22644     /**
22645      * Clean up MS wordisms...
22646      */
22647     cleanWord : function(node)
22648     {
22649         
22650         
22651         if (!node) {
22652             this.cleanWord(this.doc.body);
22653             return;
22654         }
22655         if (node.nodeName == "#text") {
22656             // clean up silly Windows -- stuff?
22657             return; 
22658         }
22659         if (node.nodeName == "#comment") {
22660             node.parentNode.removeChild(node);
22661             // clean up silly Windows -- stuff?
22662             return; 
22663         }
22664         
22665         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22666             node.parentNode.removeChild(node);
22667             return;
22668         }
22669         
22670         // remove - but keep children..
22671         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22672             while (node.childNodes.length) {
22673                 var cn = node.childNodes[0];
22674                 node.removeChild(cn);
22675                 node.parentNode.insertBefore(cn, node);
22676             }
22677             node.parentNode.removeChild(node);
22678             this.iterateChildren(node, this.cleanWord);
22679             return;
22680         }
22681         // clean styles
22682         if (node.className.length) {
22683             
22684             var cn = node.className.split(/\W+/);
22685             var cna = [];
22686             Roo.each(cn, function(cls) {
22687                 if (cls.match(/Mso[a-zA-Z]+/)) {
22688                     return;
22689                 }
22690                 cna.push(cls);
22691             });
22692             node.className = cna.length ? cna.join(' ') : '';
22693             if (!cna.length) {
22694                 node.removeAttribute("class");
22695             }
22696         }
22697         
22698         if (node.hasAttribute("lang")) {
22699             node.removeAttribute("lang");
22700         }
22701         
22702         if (node.hasAttribute("style")) {
22703             
22704             var styles = node.getAttribute("style").split(";");
22705             var nstyle = [];
22706             Roo.each(styles, function(s) {
22707                 if (!s.match(/:/)) {
22708                     return;
22709                 }
22710                 var kv = s.split(":");
22711                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22712                     return;
22713                 }
22714                 // what ever is left... we allow.
22715                 nstyle.push(s);
22716             });
22717             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22718             if (!nstyle.length) {
22719                 node.removeAttribute('style');
22720             }
22721         }
22722         this.iterateChildren(node, this.cleanWord);
22723         
22724         
22725         
22726     },
22727     /**
22728      * iterateChildren of a Node, calling fn each time, using this as the scole..
22729      * @param {DomNode} node node to iterate children of.
22730      * @param {Function} fn method of this class to call on each item.
22731      */
22732     iterateChildren : function(node, fn)
22733     {
22734         if (!node.childNodes.length) {
22735                 return;
22736         }
22737         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22738            fn.call(this, node.childNodes[i])
22739         }
22740     },
22741     
22742     
22743     /**
22744      * cleanTableWidths.
22745      *
22746      * Quite often pasting from word etc.. results in tables with column and widths.
22747      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22748      *
22749      */
22750     cleanTableWidths : function(node)
22751     {
22752          
22753          
22754         if (!node) {
22755             this.cleanTableWidths(this.doc.body);
22756             return;
22757         }
22758         
22759         // ignore list...
22760         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22761             return; 
22762         }
22763         Roo.log(node.tagName);
22764         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22765             this.iterateChildren(node, this.cleanTableWidths);
22766             return;
22767         }
22768         if (node.hasAttribute('width')) {
22769             node.removeAttribute('width');
22770         }
22771         
22772          
22773         if (node.hasAttribute("style")) {
22774             // pretty basic...
22775             
22776             var styles = node.getAttribute("style").split(";");
22777             var nstyle = [];
22778             Roo.each(styles, function(s) {
22779                 if (!s.match(/:/)) {
22780                     return;
22781                 }
22782                 var kv = s.split(":");
22783                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22784                     return;
22785                 }
22786                 // what ever is left... we allow.
22787                 nstyle.push(s);
22788             });
22789             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22790             if (!nstyle.length) {
22791                 node.removeAttribute('style');
22792             }
22793         }
22794         
22795         this.iterateChildren(node, this.cleanTableWidths);
22796         
22797         
22798     },
22799     
22800     
22801     
22802     
22803     domToHTML : function(currentElement, depth, nopadtext) {
22804         
22805         depth = depth || 0;
22806         nopadtext = nopadtext || false;
22807     
22808         if (!currentElement) {
22809             return this.domToHTML(this.doc.body);
22810         }
22811         
22812         //Roo.log(currentElement);
22813         var j;
22814         var allText = false;
22815         var nodeName = currentElement.nodeName;
22816         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22817         
22818         if  (nodeName == '#text') {
22819             
22820             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22821         }
22822         
22823         
22824         var ret = '';
22825         if (nodeName != 'BODY') {
22826              
22827             var i = 0;
22828             // Prints the node tagName, such as <A>, <IMG>, etc
22829             if (tagName) {
22830                 var attr = [];
22831                 for(i = 0; i < currentElement.attributes.length;i++) {
22832                     // quoting?
22833                     var aname = currentElement.attributes.item(i).name;
22834                     if (!currentElement.attributes.item(i).value.length) {
22835                         continue;
22836                     }
22837                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22838                 }
22839                 
22840                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22841             } 
22842             else {
22843                 
22844                 // eack
22845             }
22846         } else {
22847             tagName = false;
22848         }
22849         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22850             return ret;
22851         }
22852         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22853             nopadtext = true;
22854         }
22855         
22856         
22857         // Traverse the tree
22858         i = 0;
22859         var currentElementChild = currentElement.childNodes.item(i);
22860         var allText = true;
22861         var innerHTML  = '';
22862         lastnode = '';
22863         while (currentElementChild) {
22864             // Formatting code (indent the tree so it looks nice on the screen)
22865             var nopad = nopadtext;
22866             if (lastnode == 'SPAN') {
22867                 nopad  = true;
22868             }
22869             // text
22870             if  (currentElementChild.nodeName == '#text') {
22871                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22872                 toadd = nopadtext ? toadd : toadd.trim();
22873                 if (!nopad && toadd.length > 80) {
22874                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22875                 }
22876                 innerHTML  += toadd;
22877                 
22878                 i++;
22879                 currentElementChild = currentElement.childNodes.item(i);
22880                 lastNode = '';
22881                 continue;
22882             }
22883             allText = false;
22884             
22885             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22886                 
22887             // Recursively traverse the tree structure of the child node
22888             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22889             lastnode = currentElementChild.nodeName;
22890             i++;
22891             currentElementChild=currentElement.childNodes.item(i);
22892         }
22893         
22894         ret += innerHTML;
22895         
22896         if (!allText) {
22897                 // The remaining code is mostly for formatting the tree
22898             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22899         }
22900         
22901         
22902         if (tagName) {
22903             ret+= "</"+tagName+">";
22904         }
22905         return ret;
22906         
22907     },
22908         
22909     applyBlacklists : function()
22910     {
22911         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22912         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22913         
22914         this.white = [];
22915         this.black = [];
22916         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22917             if (b.indexOf(tag) > -1) {
22918                 return;
22919             }
22920             this.white.push(tag);
22921             
22922         }, this);
22923         
22924         Roo.each(w, function(tag) {
22925             if (b.indexOf(tag) > -1) {
22926                 return;
22927             }
22928             if (this.white.indexOf(tag) > -1) {
22929                 return;
22930             }
22931             this.white.push(tag);
22932             
22933         }, this);
22934         
22935         
22936         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22937             if (w.indexOf(tag) > -1) {
22938                 return;
22939             }
22940             this.black.push(tag);
22941             
22942         }, this);
22943         
22944         Roo.each(b, function(tag) {
22945             if (w.indexOf(tag) > -1) {
22946                 return;
22947             }
22948             if (this.black.indexOf(tag) > -1) {
22949                 return;
22950             }
22951             this.black.push(tag);
22952             
22953         }, this);
22954         
22955         
22956         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22957         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22958         
22959         this.cwhite = [];
22960         this.cblack = [];
22961         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22962             if (b.indexOf(tag) > -1) {
22963                 return;
22964             }
22965             this.cwhite.push(tag);
22966             
22967         }, this);
22968         
22969         Roo.each(w, function(tag) {
22970             if (b.indexOf(tag) > -1) {
22971                 return;
22972             }
22973             if (this.cwhite.indexOf(tag) > -1) {
22974                 return;
22975             }
22976             this.cwhite.push(tag);
22977             
22978         }, this);
22979         
22980         
22981         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22982             if (w.indexOf(tag) > -1) {
22983                 return;
22984             }
22985             this.cblack.push(tag);
22986             
22987         }, this);
22988         
22989         Roo.each(b, function(tag) {
22990             if (w.indexOf(tag) > -1) {
22991                 return;
22992             }
22993             if (this.cblack.indexOf(tag) > -1) {
22994                 return;
22995             }
22996             this.cblack.push(tag);
22997             
22998         }, this);
22999     },
23000     
23001     setStylesheets : function(stylesheets)
23002     {
23003         if(typeof(stylesheets) == 'string'){
23004             Roo.get(this.iframe.contentDocument.head).createChild({
23005                 tag : 'link',
23006                 rel : 'stylesheet',
23007                 type : 'text/css',
23008                 href : stylesheets
23009             });
23010             
23011             return;
23012         }
23013         var _this = this;
23014      
23015         Roo.each(stylesheets, function(s) {
23016             if(!s.length){
23017                 return;
23018             }
23019             
23020             Roo.get(_this.iframe.contentDocument.head).createChild({
23021                 tag : 'link',
23022                 rel : 'stylesheet',
23023                 type : 'text/css',
23024                 href : s
23025             });
23026         });
23027
23028         
23029     },
23030     
23031     removeStylesheets : function()
23032     {
23033         var _this = this;
23034         
23035         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23036             s.remove();
23037         });
23038     },
23039     
23040     setStyle : function(style)
23041     {
23042         Roo.get(this.iframe.contentDocument.head).createChild({
23043             tag : 'style',
23044             type : 'text/css',
23045             html : style
23046         });
23047
23048         return;
23049     }
23050     
23051     // hide stuff that is not compatible
23052     /**
23053      * @event blur
23054      * @hide
23055      */
23056     /**
23057      * @event change
23058      * @hide
23059      */
23060     /**
23061      * @event focus
23062      * @hide
23063      */
23064     /**
23065      * @event specialkey
23066      * @hide
23067      */
23068     /**
23069      * @cfg {String} fieldClass @hide
23070      */
23071     /**
23072      * @cfg {String} focusClass @hide
23073      */
23074     /**
23075      * @cfg {String} autoCreate @hide
23076      */
23077     /**
23078      * @cfg {String} inputType @hide
23079      */
23080     /**
23081      * @cfg {String} invalidClass @hide
23082      */
23083     /**
23084      * @cfg {String} invalidText @hide
23085      */
23086     /**
23087      * @cfg {String} msgFx @hide
23088      */
23089     /**
23090      * @cfg {String} validateOnBlur @hide
23091      */
23092 });
23093
23094 Roo.HtmlEditorCore.white = [
23095         'area', 'br', 'img', 'input', 'hr', 'wbr',
23096         
23097        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23098        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23099        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23100        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23101        'table',   'ul',         'xmp', 
23102        
23103        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23104       'thead',   'tr', 
23105      
23106       'dir', 'menu', 'ol', 'ul', 'dl',
23107        
23108       'embed',  'object'
23109 ];
23110
23111
23112 Roo.HtmlEditorCore.black = [
23113     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23114         'applet', // 
23115         'base',   'basefont', 'bgsound', 'blink',  'body', 
23116         'frame',  'frameset', 'head',    'html',   'ilayer', 
23117         'iframe', 'layer',  'link',     'meta',    'object',   
23118         'script', 'style' ,'title',  'xml' // clean later..
23119 ];
23120 Roo.HtmlEditorCore.clean = [
23121     'script', 'style', 'title', 'xml'
23122 ];
23123 Roo.HtmlEditorCore.remove = [
23124     'font'
23125 ];
23126 // attributes..
23127
23128 Roo.HtmlEditorCore.ablack = [
23129     'on'
23130 ];
23131     
23132 Roo.HtmlEditorCore.aclean = [ 
23133     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23134 ];
23135
23136 // protocols..
23137 Roo.HtmlEditorCore.pwhite= [
23138         'http',  'https',  'mailto'
23139 ];
23140
23141 // white listed style attributes.
23142 Roo.HtmlEditorCore.cwhite= [
23143       //  'text-align', /// default is to allow most things..
23144       
23145          
23146 //        'font-size'//??
23147 ];
23148
23149 // black listed style attributes.
23150 Roo.HtmlEditorCore.cblack= [
23151       //  'font-size' -- this can be set by the project 
23152 ];
23153
23154
23155 Roo.HtmlEditorCore.swapCodes   =[ 
23156     [    8211, "--" ], 
23157     [    8212, "--" ], 
23158     [    8216,  "'" ],  
23159     [    8217, "'" ],  
23160     [    8220, '"' ],  
23161     [    8221, '"' ],  
23162     [    8226, "*" ],  
23163     [    8230, "..." ]
23164 ]; 
23165
23166     /*
23167  * - LGPL
23168  *
23169  * HtmlEditor
23170  * 
23171  */
23172
23173 /**
23174  * @class Roo.bootstrap.HtmlEditor
23175  * @extends Roo.bootstrap.TextArea
23176  * Bootstrap HtmlEditor class
23177
23178  * @constructor
23179  * Create a new HtmlEditor
23180  * @param {Object} config The config object
23181  */
23182
23183 Roo.bootstrap.HtmlEditor = function(config){
23184     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23185     if (!this.toolbars) {
23186         this.toolbars = [];
23187     }
23188     
23189     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23190     this.addEvents({
23191             /**
23192              * @event initialize
23193              * Fires when the editor is fully initialized (including the iframe)
23194              * @param {HtmlEditor} this
23195              */
23196             initialize: true,
23197             /**
23198              * @event activate
23199              * Fires when the editor is first receives the focus. Any insertion must wait
23200              * until after this event.
23201              * @param {HtmlEditor} this
23202              */
23203             activate: true,
23204              /**
23205              * @event beforesync
23206              * Fires before the textarea is updated with content from the editor iframe. Return false
23207              * to cancel the sync.
23208              * @param {HtmlEditor} this
23209              * @param {String} html
23210              */
23211             beforesync: true,
23212              /**
23213              * @event beforepush
23214              * Fires before the iframe editor is updated with content from the textarea. Return false
23215              * to cancel the push.
23216              * @param {HtmlEditor} this
23217              * @param {String} html
23218              */
23219             beforepush: true,
23220              /**
23221              * @event sync
23222              * Fires when the textarea is updated with content from the editor iframe.
23223              * @param {HtmlEditor} this
23224              * @param {String} html
23225              */
23226             sync: true,
23227              /**
23228              * @event push
23229              * Fires when the iframe editor is updated with content from the textarea.
23230              * @param {HtmlEditor} this
23231              * @param {String} html
23232              */
23233             push: true,
23234              /**
23235              * @event editmodechange
23236              * Fires when the editor switches edit modes
23237              * @param {HtmlEditor} this
23238              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23239              */
23240             editmodechange: true,
23241             /**
23242              * @event editorevent
23243              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23244              * @param {HtmlEditor} this
23245              */
23246             editorevent: true,
23247             /**
23248              * @event firstfocus
23249              * Fires when on first focus - needed by toolbars..
23250              * @param {HtmlEditor} this
23251              */
23252             firstfocus: true,
23253             /**
23254              * @event autosave
23255              * Auto save the htmlEditor value as a file into Events
23256              * @param {HtmlEditor} this
23257              */
23258             autosave: true,
23259             /**
23260              * @event savedpreview
23261              * preview the saved version of htmlEditor
23262              * @param {HtmlEditor} this
23263              */
23264             savedpreview: true
23265         });
23266 };
23267
23268
23269 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23270     
23271     
23272       /**
23273      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23274      */
23275     toolbars : false,
23276     
23277      /**
23278     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23279     */
23280     btns : [],
23281    
23282      /**
23283      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23284      *                        Roo.resizable.
23285      */
23286     resizable : false,
23287      /**
23288      * @cfg {Number} height (in pixels)
23289      */   
23290     height: 300,
23291    /**
23292      * @cfg {Number} width (in pixels)
23293      */   
23294     width: false,
23295     
23296     /**
23297      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23298      * 
23299      */
23300     stylesheets: false,
23301     
23302     // id of frame..
23303     frameId: false,
23304     
23305     // private properties
23306     validationEvent : false,
23307     deferHeight: true,
23308     initialized : false,
23309     activated : false,
23310     
23311     onFocus : Roo.emptyFn,
23312     iframePad:3,
23313     hideMode:'offsets',
23314     
23315     tbContainer : false,
23316     
23317     bodyCls : '',
23318     
23319     toolbarContainer :function() {
23320         return this.wrap.select('.x-html-editor-tb',true).first();
23321     },
23322
23323     /**
23324      * Protected method that will not generally be called directly. It
23325      * is called when the editor creates its toolbar. Override this method if you need to
23326      * add custom toolbar buttons.
23327      * @param {HtmlEditor} editor
23328      */
23329     createToolbar : function(){
23330         Roo.log('renewing');
23331         Roo.log("create toolbars");
23332         
23333         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23334         this.toolbars[0].render(this.toolbarContainer());
23335         
23336         return;
23337         
23338 //        if (!editor.toolbars || !editor.toolbars.length) {
23339 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23340 //        }
23341 //        
23342 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23343 //            editor.toolbars[i] = Roo.factory(
23344 //                    typeof(editor.toolbars[i]) == 'string' ?
23345 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23346 //                Roo.bootstrap.HtmlEditor);
23347 //            editor.toolbars[i].init(editor);
23348 //        }
23349     },
23350
23351      
23352     // private
23353     onRender : function(ct, position)
23354     {
23355        // Roo.log("Call onRender: " + this.xtype);
23356         var _t = this;
23357         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23358       
23359         this.wrap = this.inputEl().wrap({
23360             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23361         });
23362         
23363         this.editorcore.onRender(ct, position);
23364          
23365         if (this.resizable) {
23366             this.resizeEl = new Roo.Resizable(this.wrap, {
23367                 pinned : true,
23368                 wrap: true,
23369                 dynamic : true,
23370                 minHeight : this.height,
23371                 height: this.height,
23372                 handles : this.resizable,
23373                 width: this.width,
23374                 listeners : {
23375                     resize : function(r, w, h) {
23376                         _t.onResize(w,h); // -something
23377                     }
23378                 }
23379             });
23380             
23381         }
23382         this.createToolbar(this);
23383        
23384         
23385         if(!this.width && this.resizable){
23386             this.setSize(this.wrap.getSize());
23387         }
23388         if (this.resizeEl) {
23389             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23390             // should trigger onReize..
23391         }
23392         
23393     },
23394
23395     // private
23396     onResize : function(w, h)
23397     {
23398         Roo.log('resize: ' +w + ',' + h );
23399         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23400         var ew = false;
23401         var eh = false;
23402         
23403         if(this.inputEl() ){
23404             if(typeof w == 'number'){
23405                 var aw = w - this.wrap.getFrameWidth('lr');
23406                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23407                 ew = aw;
23408             }
23409             if(typeof h == 'number'){
23410                  var tbh = -11;  // fixme it needs to tool bar size!
23411                 for (var i =0; i < this.toolbars.length;i++) {
23412                     // fixme - ask toolbars for heights?
23413                     tbh += this.toolbars[i].el.getHeight();
23414                     //if (this.toolbars[i].footer) {
23415                     //    tbh += this.toolbars[i].footer.el.getHeight();
23416                     //}
23417                 }
23418               
23419                 
23420                 
23421                 
23422                 
23423                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23424                 ah -= 5; // knock a few pixes off for look..
23425                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23426                 var eh = ah;
23427             }
23428         }
23429         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23430         this.editorcore.onResize(ew,eh);
23431         
23432     },
23433
23434     /**
23435      * Toggles the editor between standard and source edit mode.
23436      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23437      */
23438     toggleSourceEdit : function(sourceEditMode)
23439     {
23440         this.editorcore.toggleSourceEdit(sourceEditMode);
23441         
23442         if(this.editorcore.sourceEditMode){
23443             Roo.log('editor - showing textarea');
23444             
23445 //            Roo.log('in');
23446 //            Roo.log(this.syncValue());
23447             this.syncValue();
23448             this.inputEl().removeClass(['hide', 'x-hidden']);
23449             this.inputEl().dom.removeAttribute('tabIndex');
23450             this.inputEl().focus();
23451         }else{
23452             Roo.log('editor - hiding textarea');
23453 //            Roo.log('out')
23454 //            Roo.log(this.pushValue()); 
23455             this.pushValue();
23456             
23457             this.inputEl().addClass(['hide', 'x-hidden']);
23458             this.inputEl().dom.setAttribute('tabIndex', -1);
23459             //this.deferFocus();
23460         }
23461          
23462         if(this.resizable){
23463             this.setSize(this.wrap.getSize());
23464         }
23465         
23466         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23467     },
23468  
23469     // private (for BoxComponent)
23470     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23471
23472     // private (for BoxComponent)
23473     getResizeEl : function(){
23474         return this.wrap;
23475     },
23476
23477     // private (for BoxComponent)
23478     getPositionEl : function(){
23479         return this.wrap;
23480     },
23481
23482     // private
23483     initEvents : function(){
23484         this.originalValue = this.getValue();
23485     },
23486
23487 //    /**
23488 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23489 //     * @method
23490 //     */
23491 //    markInvalid : Roo.emptyFn,
23492 //    /**
23493 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23494 //     * @method
23495 //     */
23496 //    clearInvalid : Roo.emptyFn,
23497
23498     setValue : function(v){
23499         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23500         this.editorcore.pushValue();
23501     },
23502
23503      
23504     // private
23505     deferFocus : function(){
23506         this.focus.defer(10, this);
23507     },
23508
23509     // doc'ed in Field
23510     focus : function(){
23511         this.editorcore.focus();
23512         
23513     },
23514       
23515
23516     // private
23517     onDestroy : function(){
23518         
23519         
23520         
23521         if(this.rendered){
23522             
23523             for (var i =0; i < this.toolbars.length;i++) {
23524                 // fixme - ask toolbars for heights?
23525                 this.toolbars[i].onDestroy();
23526             }
23527             
23528             this.wrap.dom.innerHTML = '';
23529             this.wrap.remove();
23530         }
23531     },
23532
23533     // private
23534     onFirstFocus : function(){
23535         //Roo.log("onFirstFocus");
23536         this.editorcore.onFirstFocus();
23537          for (var i =0; i < this.toolbars.length;i++) {
23538             this.toolbars[i].onFirstFocus();
23539         }
23540         
23541     },
23542     
23543     // private
23544     syncValue : function()
23545     {   
23546         this.editorcore.syncValue();
23547     },
23548     
23549     pushValue : function()
23550     {   
23551         this.editorcore.pushValue();
23552     }
23553      
23554     
23555     // hide stuff that is not compatible
23556     /**
23557      * @event blur
23558      * @hide
23559      */
23560     /**
23561      * @event change
23562      * @hide
23563      */
23564     /**
23565      * @event focus
23566      * @hide
23567      */
23568     /**
23569      * @event specialkey
23570      * @hide
23571      */
23572     /**
23573      * @cfg {String} fieldClass @hide
23574      */
23575     /**
23576      * @cfg {String} focusClass @hide
23577      */
23578     /**
23579      * @cfg {String} autoCreate @hide
23580      */
23581     /**
23582      * @cfg {String} inputType @hide
23583      */
23584     /**
23585      * @cfg {String} invalidClass @hide
23586      */
23587     /**
23588      * @cfg {String} invalidText @hide
23589      */
23590     /**
23591      * @cfg {String} msgFx @hide
23592      */
23593     /**
23594      * @cfg {String} validateOnBlur @hide
23595      */
23596 });
23597  
23598     
23599    
23600    
23601    
23602       
23603 Roo.namespace('Roo.bootstrap.htmleditor');
23604 /**
23605  * @class Roo.bootstrap.HtmlEditorToolbar1
23606  * Basic Toolbar
23607  * 
23608  * Usage:
23609  *
23610  new Roo.bootstrap.HtmlEditor({
23611     ....
23612     toolbars : [
23613         new Roo.bootstrap.HtmlEditorToolbar1({
23614             disable : { fonts: 1 , format: 1, ..., ... , ...],
23615             btns : [ .... ]
23616         })
23617     }
23618      
23619  * 
23620  * @cfg {Object} disable List of elements to disable..
23621  * @cfg {Array} btns List of additional buttons.
23622  * 
23623  * 
23624  * NEEDS Extra CSS? 
23625  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23626  */
23627  
23628 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23629 {
23630     
23631     Roo.apply(this, config);
23632     
23633     // default disabled, based on 'good practice'..
23634     this.disable = this.disable || {};
23635     Roo.applyIf(this.disable, {
23636         fontSize : true,
23637         colors : true,
23638         specialElements : true
23639     });
23640     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23641     
23642     this.editor = config.editor;
23643     this.editorcore = config.editor.editorcore;
23644     
23645     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23646     
23647     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23648     // dont call parent... till later.
23649 }
23650 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23651      
23652     bar : true,
23653     
23654     editor : false,
23655     editorcore : false,
23656     
23657     
23658     formats : [
23659         "p" ,  
23660         "h1","h2","h3","h4","h5","h6", 
23661         "pre", "code", 
23662         "abbr", "acronym", "address", "cite", "samp", "var",
23663         'div','span'
23664     ],
23665     
23666     onRender : function(ct, position)
23667     {
23668        // Roo.log("Call onRender: " + this.xtype);
23669         
23670        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23671        Roo.log(this.el);
23672        this.el.dom.style.marginBottom = '0';
23673        var _this = this;
23674        var editorcore = this.editorcore;
23675        var editor= this.editor;
23676        
23677        var children = [];
23678        var btn = function(id,cmd , toggle, handler, html){
23679        
23680             var  event = toggle ? 'toggle' : 'click';
23681        
23682             var a = {
23683                 size : 'sm',
23684                 xtype: 'Button',
23685                 xns: Roo.bootstrap,
23686                 glyphicon : id,
23687                 cmd : id || cmd,
23688                 enableToggle:toggle !== false,
23689                 html : html || '',
23690                 pressed : toggle ? false : null,
23691                 listeners : {}
23692             };
23693             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23694                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23695             };
23696             children.push(a);
23697             return a;
23698        }
23699        
23700     //    var cb_box = function...
23701         
23702         var style = {
23703                 xtype: 'Button',
23704                 size : 'sm',
23705                 xns: Roo.bootstrap,
23706                 glyphicon : 'font',
23707                 //html : 'submit'
23708                 menu : {
23709                     xtype: 'Menu',
23710                     xns: Roo.bootstrap,
23711                     items:  []
23712                 }
23713         };
23714         Roo.each(this.formats, function(f) {
23715             style.menu.items.push({
23716                 xtype :'MenuItem',
23717                 xns: Roo.bootstrap,
23718                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23719                 tagname : f,
23720                 listeners : {
23721                     click : function()
23722                     {
23723                         editorcore.insertTag(this.tagname);
23724                         editor.focus();
23725                     }
23726                 }
23727                 
23728             });
23729         });
23730         children.push(style);   
23731         
23732         btn('bold',false,true);
23733         btn('italic',false,true);
23734         btn('align-left', 'justifyleft',true);
23735         btn('align-center', 'justifycenter',true);
23736         btn('align-right' , 'justifyright',true);
23737         btn('link', false, false, function(btn) {
23738             //Roo.log("create link?");
23739             var url = prompt(this.createLinkText, this.defaultLinkValue);
23740             if(url && url != 'http:/'+'/'){
23741                 this.editorcore.relayCmd('createlink', url);
23742             }
23743         }),
23744         btn('list','insertunorderedlist',true);
23745         btn('pencil', false,true, function(btn){
23746                 Roo.log(this);
23747                 this.toggleSourceEdit(btn.pressed);
23748         });
23749         
23750         if (this.editor.btns.length > 0) {
23751             for (var i = 0; i<this.editor.btns.length; i++) {
23752                 children.push(this.editor.btns[i]);
23753             }
23754         }
23755         
23756         /*
23757         var cog = {
23758                 xtype: 'Button',
23759                 size : 'sm',
23760                 xns: Roo.bootstrap,
23761                 glyphicon : 'cog',
23762                 //html : 'submit'
23763                 menu : {
23764                     xtype: 'Menu',
23765                     xns: Roo.bootstrap,
23766                     items:  []
23767                 }
23768         };
23769         
23770         cog.menu.items.push({
23771             xtype :'MenuItem',
23772             xns: Roo.bootstrap,
23773             html : Clean styles,
23774             tagname : f,
23775             listeners : {
23776                 click : function()
23777                 {
23778                     editorcore.insertTag(this.tagname);
23779                     editor.focus();
23780                 }
23781             }
23782             
23783         });
23784        */
23785         
23786          
23787        this.xtype = 'NavSimplebar';
23788         
23789         for(var i=0;i< children.length;i++) {
23790             
23791             this.buttons.add(this.addxtypeChild(children[i]));
23792             
23793         }
23794         
23795         editor.on('editorevent', this.updateToolbar, this);
23796     },
23797     onBtnClick : function(id)
23798     {
23799        this.editorcore.relayCmd(id);
23800        this.editorcore.focus();
23801     },
23802     
23803     /**
23804      * Protected method that will not generally be called directly. It triggers
23805      * a toolbar update by reading the markup state of the current selection in the editor.
23806      */
23807     updateToolbar: function(){
23808
23809         if(!this.editorcore.activated){
23810             this.editor.onFirstFocus(); // is this neeed?
23811             return;
23812         }
23813
23814         var btns = this.buttons; 
23815         var doc = this.editorcore.doc;
23816         btns.get('bold').setActive(doc.queryCommandState('bold'));
23817         btns.get('italic').setActive(doc.queryCommandState('italic'));
23818         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23819         
23820         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23821         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23822         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23823         
23824         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23825         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23826          /*
23827         
23828         var ans = this.editorcore.getAllAncestors();
23829         if (this.formatCombo) {
23830             
23831             
23832             var store = this.formatCombo.store;
23833             this.formatCombo.setValue("");
23834             for (var i =0; i < ans.length;i++) {
23835                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23836                     // select it..
23837                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23838                     break;
23839                 }
23840             }
23841         }
23842         
23843         
23844         
23845         // hides menus... - so this cant be on a menu...
23846         Roo.bootstrap.MenuMgr.hideAll();
23847         */
23848         Roo.bootstrap.MenuMgr.hideAll();
23849         //this.editorsyncValue();
23850     },
23851     onFirstFocus: function() {
23852         this.buttons.each(function(item){
23853            item.enable();
23854         });
23855     },
23856     toggleSourceEdit : function(sourceEditMode){
23857         
23858           
23859         if(sourceEditMode){
23860             Roo.log("disabling buttons");
23861            this.buttons.each( function(item){
23862                 if(item.cmd != 'pencil'){
23863                     item.disable();
23864                 }
23865             });
23866           
23867         }else{
23868             Roo.log("enabling buttons");
23869             if(this.editorcore.initialized){
23870                 this.buttons.each( function(item){
23871                     item.enable();
23872                 });
23873             }
23874             
23875         }
23876         Roo.log("calling toggole on editor");
23877         // tell the editor that it's been pressed..
23878         this.editor.toggleSourceEdit(sourceEditMode);
23879        
23880     }
23881 });
23882
23883
23884
23885
23886
23887 /**
23888  * @class Roo.bootstrap.Table.AbstractSelectionModel
23889  * @extends Roo.util.Observable
23890  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23891  * implemented by descendant classes.  This class should not be directly instantiated.
23892  * @constructor
23893  */
23894 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23895     this.locked = false;
23896     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23897 };
23898
23899
23900 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23901     /** @ignore Called by the grid automatically. Do not call directly. */
23902     init : function(grid){
23903         this.grid = grid;
23904         this.initEvents();
23905     },
23906
23907     /**
23908      * Locks the selections.
23909      */
23910     lock : function(){
23911         this.locked = true;
23912     },
23913
23914     /**
23915      * Unlocks the selections.
23916      */
23917     unlock : function(){
23918         this.locked = false;
23919     },
23920
23921     /**
23922      * Returns true if the selections are locked.
23923      * @return {Boolean}
23924      */
23925     isLocked : function(){
23926         return this.locked;
23927     }
23928 });
23929 /**
23930  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23931  * @class Roo.bootstrap.Table.RowSelectionModel
23932  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23933  * It supports multiple selections and keyboard selection/navigation. 
23934  * @constructor
23935  * @param {Object} config
23936  */
23937
23938 Roo.bootstrap.Table.RowSelectionModel = function(config){
23939     Roo.apply(this, config);
23940     this.selections = new Roo.util.MixedCollection(false, function(o){
23941         return o.id;
23942     });
23943
23944     this.last = false;
23945     this.lastActive = false;
23946
23947     this.addEvents({
23948         /**
23949              * @event selectionchange
23950              * Fires when the selection changes
23951              * @param {SelectionModel} this
23952              */
23953             "selectionchange" : true,
23954         /**
23955              * @event afterselectionchange
23956              * Fires after the selection changes (eg. by key press or clicking)
23957              * @param {SelectionModel} this
23958              */
23959             "afterselectionchange" : true,
23960         /**
23961              * @event beforerowselect
23962              * Fires when a row is selected being selected, return false to cancel.
23963              * @param {SelectionModel} this
23964              * @param {Number} rowIndex The selected index
23965              * @param {Boolean} keepExisting False if other selections will be cleared
23966              */
23967             "beforerowselect" : true,
23968         /**
23969              * @event rowselect
23970              * Fires when a row is selected.
23971              * @param {SelectionModel} this
23972              * @param {Number} rowIndex The selected index
23973              * @param {Roo.data.Record} r The record
23974              */
23975             "rowselect" : true,
23976         /**
23977              * @event rowdeselect
23978              * Fires when a row is deselected.
23979              * @param {SelectionModel} this
23980              * @param {Number} rowIndex The selected index
23981              */
23982         "rowdeselect" : true
23983     });
23984     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23985     this.locked = false;
23986  };
23987
23988 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23989     /**
23990      * @cfg {Boolean} singleSelect
23991      * True to allow selection of only one row at a time (defaults to false)
23992      */
23993     singleSelect : false,
23994
23995     // private
23996     initEvents : function()
23997     {
23998
23999         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24000         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24001         //}else{ // allow click to work like normal
24002          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24003         //}
24004         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24005         this.grid.on("rowclick", this.handleMouseDown, this);
24006         
24007         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24008             "up" : function(e){
24009                 if(!e.shiftKey){
24010                     this.selectPrevious(e.shiftKey);
24011                 }else if(this.last !== false && this.lastActive !== false){
24012                     var last = this.last;
24013                     this.selectRange(this.last,  this.lastActive-1);
24014                     this.grid.getView().focusRow(this.lastActive);
24015                     if(last !== false){
24016                         this.last = last;
24017                     }
24018                 }else{
24019                     this.selectFirstRow();
24020                 }
24021                 this.fireEvent("afterselectionchange", this);
24022             },
24023             "down" : function(e){
24024                 if(!e.shiftKey){
24025                     this.selectNext(e.shiftKey);
24026                 }else if(this.last !== false && this.lastActive !== false){
24027                     var last = this.last;
24028                     this.selectRange(this.last,  this.lastActive+1);
24029                     this.grid.getView().focusRow(this.lastActive);
24030                     if(last !== false){
24031                         this.last = last;
24032                     }
24033                 }else{
24034                     this.selectFirstRow();
24035                 }
24036                 this.fireEvent("afterselectionchange", this);
24037             },
24038             scope: this
24039         });
24040         this.grid.store.on('load', function(){
24041             this.selections.clear();
24042         },this);
24043         /*
24044         var view = this.grid.view;
24045         view.on("refresh", this.onRefresh, this);
24046         view.on("rowupdated", this.onRowUpdated, this);
24047         view.on("rowremoved", this.onRemove, this);
24048         */
24049     },
24050
24051     // private
24052     onRefresh : function()
24053     {
24054         var ds = this.grid.store, i, v = this.grid.view;
24055         var s = this.selections;
24056         s.each(function(r){
24057             if((i = ds.indexOfId(r.id)) != -1){
24058                 v.onRowSelect(i);
24059             }else{
24060                 s.remove(r);
24061             }
24062         });
24063     },
24064
24065     // private
24066     onRemove : function(v, index, r){
24067         this.selections.remove(r);
24068     },
24069
24070     // private
24071     onRowUpdated : function(v, index, r){
24072         if(this.isSelected(r)){
24073             v.onRowSelect(index);
24074         }
24075     },
24076
24077     /**
24078      * Select records.
24079      * @param {Array} records The records to select
24080      * @param {Boolean} keepExisting (optional) True to keep existing selections
24081      */
24082     selectRecords : function(records, keepExisting)
24083     {
24084         if(!keepExisting){
24085             this.clearSelections();
24086         }
24087             var ds = this.grid.store;
24088         for(var i = 0, len = records.length; i < len; i++){
24089             this.selectRow(ds.indexOf(records[i]), true);
24090         }
24091     },
24092
24093     /**
24094      * Gets the number of selected rows.
24095      * @return {Number}
24096      */
24097     getCount : function(){
24098         return this.selections.length;
24099     },
24100
24101     /**
24102      * Selects the first row in the grid.
24103      */
24104     selectFirstRow : function(){
24105         this.selectRow(0);
24106     },
24107
24108     /**
24109      * Select the last row.
24110      * @param {Boolean} keepExisting (optional) True to keep existing selections
24111      */
24112     selectLastRow : function(keepExisting){
24113         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24114         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24115     },
24116
24117     /**
24118      * Selects the row immediately following the last selected row.
24119      * @param {Boolean} keepExisting (optional) True to keep existing selections
24120      */
24121     selectNext : function(keepExisting)
24122     {
24123             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24124             this.selectRow(this.last+1, keepExisting);
24125             this.grid.getView().focusRow(this.last);
24126         }
24127     },
24128
24129     /**
24130      * Selects the row that precedes the last selected row.
24131      * @param {Boolean} keepExisting (optional) True to keep existing selections
24132      */
24133     selectPrevious : function(keepExisting){
24134         if(this.last){
24135             this.selectRow(this.last-1, keepExisting);
24136             this.grid.getView().focusRow(this.last);
24137         }
24138     },
24139
24140     /**
24141      * Returns the selected records
24142      * @return {Array} Array of selected records
24143      */
24144     getSelections : function(){
24145         return [].concat(this.selections.items);
24146     },
24147
24148     /**
24149      * Returns the first selected record.
24150      * @return {Record}
24151      */
24152     getSelected : function(){
24153         return this.selections.itemAt(0);
24154     },
24155
24156
24157     /**
24158      * Clears all selections.
24159      */
24160     clearSelections : function(fast)
24161     {
24162         if(this.locked) {
24163             return;
24164         }
24165         if(fast !== true){
24166                 var ds = this.grid.store;
24167             var s = this.selections;
24168             s.each(function(r){
24169                 this.deselectRow(ds.indexOfId(r.id));
24170             }, this);
24171             s.clear();
24172         }else{
24173             this.selections.clear();
24174         }
24175         this.last = false;
24176     },
24177
24178
24179     /**
24180      * Selects all rows.
24181      */
24182     selectAll : function(){
24183         if(this.locked) {
24184             return;
24185         }
24186         this.selections.clear();
24187         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24188             this.selectRow(i, true);
24189         }
24190     },
24191
24192     /**
24193      * Returns True if there is a selection.
24194      * @return {Boolean}
24195      */
24196     hasSelection : function(){
24197         return this.selections.length > 0;
24198     },
24199
24200     /**
24201      * Returns True if the specified row is selected.
24202      * @param {Number/Record} record The record or index of the record to check
24203      * @return {Boolean}
24204      */
24205     isSelected : function(index){
24206             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24207         return (r && this.selections.key(r.id) ? true : false);
24208     },
24209
24210     /**
24211      * Returns True if the specified record id is selected.
24212      * @param {String} id The id of record to check
24213      * @return {Boolean}
24214      */
24215     isIdSelected : function(id){
24216         return (this.selections.key(id) ? true : false);
24217     },
24218
24219
24220     // private
24221     handleMouseDBClick : function(e, t){
24222         
24223     },
24224     // private
24225     handleMouseDown : function(e, t)
24226     {
24227             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24228         if(this.isLocked() || rowIndex < 0 ){
24229             return;
24230         };
24231         if(e.shiftKey && this.last !== false){
24232             var last = this.last;
24233             this.selectRange(last, rowIndex, e.ctrlKey);
24234             this.last = last; // reset the last
24235             t.focus();
24236     
24237         }else{
24238             var isSelected = this.isSelected(rowIndex);
24239             //Roo.log("select row:" + rowIndex);
24240             if(isSelected){
24241                 this.deselectRow(rowIndex);
24242             } else {
24243                         this.selectRow(rowIndex, true);
24244             }
24245     
24246             /*
24247                 if(e.button !== 0 && isSelected){
24248                 alert('rowIndex 2: ' + rowIndex);
24249                     view.focusRow(rowIndex);
24250                 }else if(e.ctrlKey && isSelected){
24251                     this.deselectRow(rowIndex);
24252                 }else if(!isSelected){
24253                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24254                     view.focusRow(rowIndex);
24255                 }
24256             */
24257         }
24258         this.fireEvent("afterselectionchange", this);
24259     },
24260     // private
24261     handleDragableRowClick :  function(grid, rowIndex, e) 
24262     {
24263         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24264             this.selectRow(rowIndex, false);
24265             grid.view.focusRow(rowIndex);
24266              this.fireEvent("afterselectionchange", this);
24267         }
24268     },
24269     
24270     /**
24271      * Selects multiple rows.
24272      * @param {Array} rows Array of the indexes of the row to select
24273      * @param {Boolean} keepExisting (optional) True to keep existing selections
24274      */
24275     selectRows : function(rows, keepExisting){
24276         if(!keepExisting){
24277             this.clearSelections();
24278         }
24279         for(var i = 0, len = rows.length; i < len; i++){
24280             this.selectRow(rows[i], true);
24281         }
24282     },
24283
24284     /**
24285      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24286      * @param {Number} startRow The index of the first row in the range
24287      * @param {Number} endRow The index of the last row in the range
24288      * @param {Boolean} keepExisting (optional) True to retain existing selections
24289      */
24290     selectRange : function(startRow, endRow, keepExisting){
24291         if(this.locked) {
24292             return;
24293         }
24294         if(!keepExisting){
24295             this.clearSelections();
24296         }
24297         if(startRow <= endRow){
24298             for(var i = startRow; i <= endRow; i++){
24299                 this.selectRow(i, true);
24300             }
24301         }else{
24302             for(var i = startRow; i >= endRow; i--){
24303                 this.selectRow(i, true);
24304             }
24305         }
24306     },
24307
24308     /**
24309      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24310      * @param {Number} startRow The index of the first row in the range
24311      * @param {Number} endRow The index of the last row in the range
24312      */
24313     deselectRange : function(startRow, endRow, preventViewNotify){
24314         if(this.locked) {
24315             return;
24316         }
24317         for(var i = startRow; i <= endRow; i++){
24318             this.deselectRow(i, preventViewNotify);
24319         }
24320     },
24321
24322     /**
24323      * Selects a row.
24324      * @param {Number} row The index of the row to select
24325      * @param {Boolean} keepExisting (optional) True to keep existing selections
24326      */
24327     selectRow : function(index, keepExisting, preventViewNotify)
24328     {
24329             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24330             return;
24331         }
24332         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24333             if(!keepExisting || this.singleSelect){
24334                 this.clearSelections();
24335             }
24336             
24337             var r = this.grid.store.getAt(index);
24338             //console.log('selectRow - record id :' + r.id);
24339             
24340             this.selections.add(r);
24341             this.last = this.lastActive = index;
24342             if(!preventViewNotify){
24343                 var proxy = new Roo.Element(
24344                                 this.grid.getRowDom(index)
24345                 );
24346                 proxy.addClass('bg-info info');
24347             }
24348             this.fireEvent("rowselect", this, index, r);
24349             this.fireEvent("selectionchange", this);
24350         }
24351     },
24352
24353     /**
24354      * Deselects a row.
24355      * @param {Number} row The index of the row to deselect
24356      */
24357     deselectRow : function(index, preventViewNotify)
24358     {
24359         if(this.locked) {
24360             return;
24361         }
24362         if(this.last == index){
24363             this.last = false;
24364         }
24365         if(this.lastActive == index){
24366             this.lastActive = false;
24367         }
24368         
24369         var r = this.grid.store.getAt(index);
24370         if (!r) {
24371             return;
24372         }
24373         
24374         this.selections.remove(r);
24375         //.console.log('deselectRow - record id :' + r.id);
24376         if(!preventViewNotify){
24377         
24378             var proxy = new Roo.Element(
24379                 this.grid.getRowDom(index)
24380             );
24381             proxy.removeClass('bg-info info');
24382         }
24383         this.fireEvent("rowdeselect", this, index);
24384         this.fireEvent("selectionchange", this);
24385     },
24386
24387     // private
24388     restoreLast : function(){
24389         if(this._last){
24390             this.last = this._last;
24391         }
24392     },
24393
24394     // private
24395     acceptsNav : function(row, col, cm){
24396         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24397     },
24398
24399     // private
24400     onEditorKey : function(field, e){
24401         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24402         if(k == e.TAB){
24403             e.stopEvent();
24404             ed.completeEdit();
24405             if(e.shiftKey){
24406                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24407             }else{
24408                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24409             }
24410         }else if(k == e.ENTER && !e.ctrlKey){
24411             e.stopEvent();
24412             ed.completeEdit();
24413             if(e.shiftKey){
24414                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24415             }else{
24416                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24417             }
24418         }else if(k == e.ESC){
24419             ed.cancelEdit();
24420         }
24421         if(newCell){
24422             g.startEditing(newCell[0], newCell[1]);
24423         }
24424     }
24425 });
24426 /*
24427  * Based on:
24428  * Ext JS Library 1.1.1
24429  * Copyright(c) 2006-2007, Ext JS, LLC.
24430  *
24431  * Originally Released Under LGPL - original licence link has changed is not relivant.
24432  *
24433  * Fork - LGPL
24434  * <script type="text/javascript">
24435  */
24436  
24437 /**
24438  * @class Roo.bootstrap.PagingToolbar
24439  * @extends Roo.bootstrap.NavSimplebar
24440  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24441  * @constructor
24442  * Create a new PagingToolbar
24443  * @param {Object} config The config object
24444  * @param {Roo.data.Store} store
24445  */
24446 Roo.bootstrap.PagingToolbar = function(config)
24447 {
24448     // old args format still supported... - xtype is prefered..
24449         // created from xtype...
24450     
24451     this.ds = config.dataSource;
24452     
24453     if (config.store && !this.ds) {
24454         this.store= Roo.factory(config.store, Roo.data);
24455         this.ds = this.store;
24456         this.ds.xmodule = this.xmodule || false;
24457     }
24458     
24459     this.toolbarItems = [];
24460     if (config.items) {
24461         this.toolbarItems = config.items;
24462     }
24463     
24464     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24465     
24466     this.cursor = 0;
24467     
24468     if (this.ds) { 
24469         this.bind(this.ds);
24470     }
24471     
24472     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24473     
24474 };
24475
24476 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24477     /**
24478      * @cfg {Roo.data.Store} dataSource
24479      * The underlying data store providing the paged data
24480      */
24481     /**
24482      * @cfg {String/HTMLElement/Element} container
24483      * container The id or element that will contain the toolbar
24484      */
24485     /**
24486      * @cfg {Boolean} displayInfo
24487      * True to display the displayMsg (defaults to false)
24488      */
24489     /**
24490      * @cfg {Number} pageSize
24491      * The number of records to display per page (defaults to 20)
24492      */
24493     pageSize: 20,
24494     /**
24495      * @cfg {String} displayMsg
24496      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24497      */
24498     displayMsg : 'Displaying {0} - {1} of {2}',
24499     /**
24500      * @cfg {String} emptyMsg
24501      * The message to display when no records are found (defaults to "No data to display")
24502      */
24503     emptyMsg : 'No data to display',
24504     /**
24505      * Customizable piece of the default paging text (defaults to "Page")
24506      * @type String
24507      */
24508     beforePageText : "Page",
24509     /**
24510      * Customizable piece of the default paging text (defaults to "of %0")
24511      * @type String
24512      */
24513     afterPageText : "of {0}",
24514     /**
24515      * Customizable piece of the default paging text (defaults to "First Page")
24516      * @type String
24517      */
24518     firstText : "First Page",
24519     /**
24520      * Customizable piece of the default paging text (defaults to "Previous Page")
24521      * @type String
24522      */
24523     prevText : "Previous Page",
24524     /**
24525      * Customizable piece of the default paging text (defaults to "Next Page")
24526      * @type String
24527      */
24528     nextText : "Next Page",
24529     /**
24530      * Customizable piece of the default paging text (defaults to "Last Page")
24531      * @type String
24532      */
24533     lastText : "Last Page",
24534     /**
24535      * Customizable piece of the default paging text (defaults to "Refresh")
24536      * @type String
24537      */
24538     refreshText : "Refresh",
24539
24540     buttons : false,
24541     // private
24542     onRender : function(ct, position) 
24543     {
24544         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24545         this.navgroup.parentId = this.id;
24546         this.navgroup.onRender(this.el, null);
24547         // add the buttons to the navgroup
24548         
24549         if(this.displayInfo){
24550             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24551             this.displayEl = this.el.select('.x-paging-info', true).first();
24552 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24553 //            this.displayEl = navel.el.select('span',true).first();
24554         }
24555         
24556         var _this = this;
24557         
24558         if(this.buttons){
24559             Roo.each(_this.buttons, function(e){ // this might need to use render????
24560                Roo.factory(e).render(_this.el);
24561             });
24562         }
24563             
24564         Roo.each(_this.toolbarItems, function(e) {
24565             _this.navgroup.addItem(e);
24566         });
24567         
24568         
24569         this.first = this.navgroup.addItem({
24570             tooltip: this.firstText,
24571             cls: "prev",
24572             icon : 'fa fa-backward',
24573             disabled: true,
24574             preventDefault: true,
24575             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24576         });
24577         
24578         this.prev =  this.navgroup.addItem({
24579             tooltip: this.prevText,
24580             cls: "prev",
24581             icon : 'fa fa-step-backward',
24582             disabled: true,
24583             preventDefault: true,
24584             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24585         });
24586     //this.addSeparator();
24587         
24588         
24589         var field = this.navgroup.addItem( {
24590             tagtype : 'span',
24591             cls : 'x-paging-position',
24592             
24593             html : this.beforePageText  +
24594                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24595                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24596          } ); //?? escaped?
24597         
24598         this.field = field.el.select('input', true).first();
24599         this.field.on("keydown", this.onPagingKeydown, this);
24600         this.field.on("focus", function(){this.dom.select();});
24601     
24602     
24603         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24604         //this.field.setHeight(18);
24605         //this.addSeparator();
24606         this.next = this.navgroup.addItem({
24607             tooltip: this.nextText,
24608             cls: "next",
24609             html : ' <i class="fa fa-step-forward">',
24610             disabled: true,
24611             preventDefault: true,
24612             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24613         });
24614         this.last = this.navgroup.addItem({
24615             tooltip: this.lastText,
24616             icon : 'fa fa-forward',
24617             cls: "next",
24618             disabled: true,
24619             preventDefault: true,
24620             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24621         });
24622     //this.addSeparator();
24623         this.loading = this.navgroup.addItem({
24624             tooltip: this.refreshText,
24625             icon: 'fa fa-refresh',
24626             preventDefault: true,
24627             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24628         });
24629         
24630     },
24631
24632     // private
24633     updateInfo : function(){
24634         if(this.displayEl){
24635             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24636             var msg = count == 0 ?
24637                 this.emptyMsg :
24638                 String.format(
24639                     this.displayMsg,
24640                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24641                 );
24642             this.displayEl.update(msg);
24643         }
24644     },
24645
24646     // private
24647     onLoad : function(ds, r, o)
24648     {
24649         this.cursor = o.params.start ? o.params.start : 0;
24650         
24651         var d = this.getPageData(),
24652             ap = d.activePage,
24653             ps = d.pages;
24654         
24655         
24656         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24657         this.field.dom.value = ap;
24658         this.first.setDisabled(ap == 1);
24659         this.prev.setDisabled(ap == 1);
24660         this.next.setDisabled(ap == ps);
24661         this.last.setDisabled(ap == ps);
24662         this.loading.enable();
24663         this.updateInfo();
24664     },
24665
24666     // private
24667     getPageData : function(){
24668         var total = this.ds.getTotalCount();
24669         return {
24670             total : total,
24671             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24672             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24673         };
24674     },
24675
24676     // private
24677     onLoadError : function(){
24678         this.loading.enable();
24679     },
24680
24681     // private
24682     onPagingKeydown : function(e){
24683         var k = e.getKey();
24684         var d = this.getPageData();
24685         if(k == e.RETURN){
24686             var v = this.field.dom.value, pageNum;
24687             if(!v || isNaN(pageNum = parseInt(v, 10))){
24688                 this.field.dom.value = d.activePage;
24689                 return;
24690             }
24691             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24692             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24693             e.stopEvent();
24694         }
24695         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))
24696         {
24697           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24698           this.field.dom.value = pageNum;
24699           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24700           e.stopEvent();
24701         }
24702         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24703         {
24704           var v = this.field.dom.value, pageNum; 
24705           var increment = (e.shiftKey) ? 10 : 1;
24706           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24707                 increment *= -1;
24708           }
24709           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24710             this.field.dom.value = d.activePage;
24711             return;
24712           }
24713           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24714           {
24715             this.field.dom.value = parseInt(v, 10) + increment;
24716             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24717             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24718           }
24719           e.stopEvent();
24720         }
24721     },
24722
24723     // private
24724     beforeLoad : function(){
24725         if(this.loading){
24726             this.loading.disable();
24727         }
24728     },
24729
24730     // private
24731     onClick : function(which){
24732         
24733         var ds = this.ds;
24734         if (!ds) {
24735             return;
24736         }
24737         
24738         switch(which){
24739             case "first":
24740                 ds.load({params:{start: 0, limit: this.pageSize}});
24741             break;
24742             case "prev":
24743                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24744             break;
24745             case "next":
24746                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24747             break;
24748             case "last":
24749                 var total = ds.getTotalCount();
24750                 var extra = total % this.pageSize;
24751                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24752                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24753             break;
24754             case "refresh":
24755                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24756             break;
24757         }
24758     },
24759
24760     /**
24761      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24762      * @param {Roo.data.Store} store The data store to unbind
24763      */
24764     unbind : function(ds){
24765         ds.un("beforeload", this.beforeLoad, this);
24766         ds.un("load", this.onLoad, this);
24767         ds.un("loadexception", this.onLoadError, this);
24768         ds.un("remove", this.updateInfo, this);
24769         ds.un("add", this.updateInfo, this);
24770         this.ds = undefined;
24771     },
24772
24773     /**
24774      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24775      * @param {Roo.data.Store} store The data store to bind
24776      */
24777     bind : function(ds){
24778         ds.on("beforeload", this.beforeLoad, this);
24779         ds.on("load", this.onLoad, this);
24780         ds.on("loadexception", this.onLoadError, this);
24781         ds.on("remove", this.updateInfo, this);
24782         ds.on("add", this.updateInfo, this);
24783         this.ds = ds;
24784     }
24785 });/*
24786  * - LGPL
24787  *
24788  * element
24789  * 
24790  */
24791
24792 /**
24793  * @class Roo.bootstrap.MessageBar
24794  * @extends Roo.bootstrap.Component
24795  * Bootstrap MessageBar class
24796  * @cfg {String} html contents of the MessageBar
24797  * @cfg {String} weight (info | success | warning | danger) default info
24798  * @cfg {String} beforeClass insert the bar before the given class
24799  * @cfg {Boolean} closable (true | false) default false
24800  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24801  * 
24802  * @constructor
24803  * Create a new Element
24804  * @param {Object} config The config object
24805  */
24806
24807 Roo.bootstrap.MessageBar = function(config){
24808     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24809 };
24810
24811 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24812     
24813     html: '',
24814     weight: 'info',
24815     closable: false,
24816     fixed: false,
24817     beforeClass: 'bootstrap-sticky-wrap',
24818     
24819     getAutoCreate : function(){
24820         
24821         var cfg = {
24822             tag: 'div',
24823             cls: 'alert alert-dismissable alert-' + this.weight,
24824             cn: [
24825                 {
24826                     tag: 'span',
24827                     cls: 'message',
24828                     html: this.html || ''
24829                 }
24830             ]
24831         };
24832         
24833         if(this.fixed){
24834             cfg.cls += ' alert-messages-fixed';
24835         }
24836         
24837         if(this.closable){
24838             cfg.cn.push({
24839                 tag: 'button',
24840                 cls: 'close',
24841                 html: 'x'
24842             });
24843         }
24844         
24845         return cfg;
24846     },
24847     
24848     onRender : function(ct, position)
24849     {
24850         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24851         
24852         if(!this.el){
24853             var cfg = Roo.apply({},  this.getAutoCreate());
24854             cfg.id = Roo.id();
24855             
24856             if (this.cls) {
24857                 cfg.cls += ' ' + this.cls;
24858             }
24859             if (this.style) {
24860                 cfg.style = this.style;
24861             }
24862             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24863             
24864             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24865         }
24866         
24867         this.el.select('>button.close').on('click', this.hide, this);
24868         
24869     },
24870     
24871     show : function()
24872     {
24873         if (!this.rendered) {
24874             this.render();
24875         }
24876         
24877         this.el.show();
24878         
24879         this.fireEvent('show', this);
24880         
24881     },
24882     
24883     hide : function()
24884     {
24885         if (!this.rendered) {
24886             this.render();
24887         }
24888         
24889         this.el.hide();
24890         
24891         this.fireEvent('hide', this);
24892     },
24893     
24894     update : function()
24895     {
24896 //        var e = this.el.dom.firstChild;
24897 //        
24898 //        if(this.closable){
24899 //            e = e.nextSibling;
24900 //        }
24901 //        
24902 //        e.data = this.html || '';
24903
24904         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24905     }
24906    
24907 });
24908
24909  
24910
24911      /*
24912  * - LGPL
24913  *
24914  * Graph
24915  * 
24916  */
24917
24918
24919 /**
24920  * @class Roo.bootstrap.Graph
24921  * @extends Roo.bootstrap.Component
24922  * Bootstrap Graph class
24923 > Prameters
24924  -sm {number} sm 4
24925  -md {number} md 5
24926  @cfg {String} graphtype  bar | vbar | pie
24927  @cfg {number} g_x coodinator | centre x (pie)
24928  @cfg {number} g_y coodinator | centre y (pie)
24929  @cfg {number} g_r radius (pie)
24930  @cfg {number} g_height height of the chart (respected by all elements in the set)
24931  @cfg {number} g_width width of the chart (respected by all elements in the set)
24932  @cfg {Object} title The title of the chart
24933     
24934  -{Array}  values
24935  -opts (object) options for the chart 
24936      o {
24937      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24938      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24939      o vgutter (number)
24940      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.
24941      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24942      o to
24943      o stretch (boolean)
24944      o }
24945  -opts (object) options for the pie
24946      o{
24947      o cut
24948      o startAngle (number)
24949      o endAngle (number)
24950      } 
24951  *
24952  * @constructor
24953  * Create a new Input
24954  * @param {Object} config The config object
24955  */
24956
24957 Roo.bootstrap.Graph = function(config){
24958     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24959     
24960     this.addEvents({
24961         // img events
24962         /**
24963          * @event click
24964          * The img click event for the img.
24965          * @param {Roo.EventObject} e
24966          */
24967         "click" : true
24968     });
24969 };
24970
24971 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24972     
24973     sm: 4,
24974     md: 5,
24975     graphtype: 'bar',
24976     g_height: 250,
24977     g_width: 400,
24978     g_x: 50,
24979     g_y: 50,
24980     g_r: 30,
24981     opts:{
24982         //g_colors: this.colors,
24983         g_type: 'soft',
24984         g_gutter: '20%'
24985
24986     },
24987     title : false,
24988
24989     getAutoCreate : function(){
24990         
24991         var cfg = {
24992             tag: 'div',
24993             html : null
24994         };
24995         
24996         
24997         return  cfg;
24998     },
24999
25000     onRender : function(ct,position){
25001         
25002         
25003         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25004         
25005         if (typeof(Raphael) == 'undefined') {
25006             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25007             return;
25008         }
25009         
25010         this.raphael = Raphael(this.el.dom);
25011         
25012                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25013                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25014                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25015                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25016                 /*
25017                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25018                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25019                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25020                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25021                 
25022                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25023                 r.barchart(330, 10, 300, 220, data1);
25024                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25025                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25026                 */
25027                 
25028                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25029                 // r.barchart(30, 30, 560, 250,  xdata, {
25030                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25031                 //     axis : "0 0 1 1",
25032                 //     axisxlabels :  xdata
25033                 //     //yvalues : cols,
25034                    
25035                 // });
25036 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25037 //        
25038 //        this.load(null,xdata,{
25039 //                axis : "0 0 1 1",
25040 //                axisxlabels :  xdata
25041 //                });
25042
25043     },
25044
25045     load : function(graphtype,xdata,opts)
25046     {
25047         this.raphael.clear();
25048         if(!graphtype) {
25049             graphtype = this.graphtype;
25050         }
25051         if(!opts){
25052             opts = this.opts;
25053         }
25054         var r = this.raphael,
25055             fin = function () {
25056                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25057             },
25058             fout = function () {
25059                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25060             },
25061             pfin = function() {
25062                 this.sector.stop();
25063                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25064
25065                 if (this.label) {
25066                     this.label[0].stop();
25067                     this.label[0].attr({ r: 7.5 });
25068                     this.label[1].attr({ "font-weight": 800 });
25069                 }
25070             },
25071             pfout = function() {
25072                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25073
25074                 if (this.label) {
25075                     this.label[0].animate({ r: 5 }, 500, "bounce");
25076                     this.label[1].attr({ "font-weight": 400 });
25077                 }
25078             };
25079
25080         switch(graphtype){
25081             case 'bar':
25082                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25083                 break;
25084             case 'hbar':
25085                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25086                 break;
25087             case 'pie':
25088 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25089 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25090 //            
25091                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25092                 
25093                 break;
25094
25095         }
25096         
25097         if(this.title){
25098             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25099         }
25100         
25101     },
25102     
25103     setTitle: function(o)
25104     {
25105         this.title = o;
25106     },
25107     
25108     initEvents: function() {
25109         
25110         if(!this.href){
25111             this.el.on('click', this.onClick, this);
25112         }
25113     },
25114     
25115     onClick : function(e)
25116     {
25117         Roo.log('img onclick');
25118         this.fireEvent('click', this, e);
25119     }
25120    
25121 });
25122
25123  
25124 /*
25125  * - LGPL
25126  *
25127  * numberBox
25128  * 
25129  */
25130 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25131
25132 /**
25133  * @class Roo.bootstrap.dash.NumberBox
25134  * @extends Roo.bootstrap.Component
25135  * Bootstrap NumberBox class
25136  * @cfg {String} headline Box headline
25137  * @cfg {String} content Box content
25138  * @cfg {String} icon Box icon
25139  * @cfg {String} footer Footer text
25140  * @cfg {String} fhref Footer href
25141  * 
25142  * @constructor
25143  * Create a new NumberBox
25144  * @param {Object} config The config object
25145  */
25146
25147
25148 Roo.bootstrap.dash.NumberBox = function(config){
25149     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25150     
25151 };
25152
25153 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25154     
25155     headline : '',
25156     content : '',
25157     icon : '',
25158     footer : '',
25159     fhref : '',
25160     ficon : '',
25161     
25162     getAutoCreate : function(){
25163         
25164         var cfg = {
25165             tag : 'div',
25166             cls : 'small-box ',
25167             cn : [
25168                 {
25169                     tag : 'div',
25170                     cls : 'inner',
25171                     cn :[
25172                         {
25173                             tag : 'h3',
25174                             cls : 'roo-headline',
25175                             html : this.headline
25176                         },
25177                         {
25178                             tag : 'p',
25179                             cls : 'roo-content',
25180                             html : this.content
25181                         }
25182                     ]
25183                 }
25184             ]
25185         };
25186         
25187         if(this.icon){
25188             cfg.cn.push({
25189                 tag : 'div',
25190                 cls : 'icon',
25191                 cn :[
25192                     {
25193                         tag : 'i',
25194                         cls : 'ion ' + this.icon
25195                     }
25196                 ]
25197             });
25198         }
25199         
25200         if(this.footer){
25201             var footer = {
25202                 tag : 'a',
25203                 cls : 'small-box-footer',
25204                 href : this.fhref || '#',
25205                 html : this.footer
25206             };
25207             
25208             cfg.cn.push(footer);
25209             
25210         }
25211         
25212         return  cfg;
25213     },
25214
25215     onRender : function(ct,position){
25216         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25217
25218
25219        
25220                 
25221     },
25222
25223     setHeadline: function (value)
25224     {
25225         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25226     },
25227     
25228     setFooter: function (value, href)
25229     {
25230         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25231         
25232         if(href){
25233             this.el.select('a.small-box-footer',true).first().attr('href', href);
25234         }
25235         
25236     },
25237
25238     setContent: function (value)
25239     {
25240         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25241     },
25242
25243     initEvents: function() 
25244     {   
25245         
25246     }
25247     
25248 });
25249
25250  
25251 /*
25252  * - LGPL
25253  *
25254  * TabBox
25255  * 
25256  */
25257 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25258
25259 /**
25260  * @class Roo.bootstrap.dash.TabBox
25261  * @extends Roo.bootstrap.Component
25262  * Bootstrap TabBox class
25263  * @cfg {String} title Title of the TabBox
25264  * @cfg {String} icon Icon of the TabBox
25265  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25266  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25267  * 
25268  * @constructor
25269  * Create a new TabBox
25270  * @param {Object} config The config object
25271  */
25272
25273
25274 Roo.bootstrap.dash.TabBox = function(config){
25275     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25276     this.addEvents({
25277         // raw events
25278         /**
25279          * @event addpane
25280          * When a pane is added
25281          * @param {Roo.bootstrap.dash.TabPane} pane
25282          */
25283         "addpane" : true,
25284         /**
25285          * @event activatepane
25286          * When a pane is activated
25287          * @param {Roo.bootstrap.dash.TabPane} pane
25288          */
25289         "activatepane" : true
25290         
25291          
25292     });
25293     
25294     this.panes = [];
25295 };
25296
25297 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25298
25299     title : '',
25300     icon : false,
25301     showtabs : true,
25302     tabScrollable : false,
25303     
25304     getChildContainer : function()
25305     {
25306         return this.el.select('.tab-content', true).first();
25307     },
25308     
25309     getAutoCreate : function(){
25310         
25311         var header = {
25312             tag: 'li',
25313             cls: 'pull-left header',
25314             html: this.title,
25315             cn : []
25316         };
25317         
25318         if(this.icon){
25319             header.cn.push({
25320                 tag: 'i',
25321                 cls: 'fa ' + this.icon
25322             });
25323         }
25324         
25325         var h = {
25326             tag: 'ul',
25327             cls: 'nav nav-tabs pull-right',
25328             cn: [
25329                 header
25330             ]
25331         };
25332         
25333         if(this.tabScrollable){
25334             h = {
25335                 tag: 'div',
25336                 cls: 'tab-header',
25337                 cn: [
25338                     {
25339                         tag: 'ul',
25340                         cls: 'nav nav-tabs pull-right',
25341                         cn: [
25342                             header
25343                         ]
25344                     }
25345                 ]
25346             };
25347         }
25348         
25349         var cfg = {
25350             tag: 'div',
25351             cls: 'nav-tabs-custom',
25352             cn: [
25353                 h,
25354                 {
25355                     tag: 'div',
25356                     cls: 'tab-content no-padding',
25357                     cn: []
25358                 }
25359             ]
25360         };
25361
25362         return  cfg;
25363     },
25364     initEvents : function()
25365     {
25366         //Roo.log('add add pane handler');
25367         this.on('addpane', this.onAddPane, this);
25368     },
25369      /**
25370      * Updates the box title
25371      * @param {String} html to set the title to.
25372      */
25373     setTitle : function(value)
25374     {
25375         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25376     },
25377     onAddPane : function(pane)
25378     {
25379         this.panes.push(pane);
25380         //Roo.log('addpane');
25381         //Roo.log(pane);
25382         // tabs are rendere left to right..
25383         if(!this.showtabs){
25384             return;
25385         }
25386         
25387         var ctr = this.el.select('.nav-tabs', true).first();
25388          
25389          
25390         var existing = ctr.select('.nav-tab',true);
25391         var qty = existing.getCount();;
25392         
25393         
25394         var tab = ctr.createChild({
25395             tag : 'li',
25396             cls : 'nav-tab' + (qty ? '' : ' active'),
25397             cn : [
25398                 {
25399                     tag : 'a',
25400                     href:'#',
25401                     html : pane.title
25402                 }
25403             ]
25404         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25405         pane.tab = tab;
25406         
25407         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25408         if (!qty) {
25409             pane.el.addClass('active');
25410         }
25411         
25412                 
25413     },
25414     onTabClick : function(ev,un,ob,pane)
25415     {
25416         //Roo.log('tab - prev default');
25417         ev.preventDefault();
25418         
25419         
25420         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25421         pane.tab.addClass('active');
25422         //Roo.log(pane.title);
25423         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25424         // technically we should have a deactivate event.. but maybe add later.
25425         // and it should not de-activate the selected tab...
25426         this.fireEvent('activatepane', pane);
25427         pane.el.addClass('active');
25428         pane.fireEvent('activate');
25429         
25430         
25431     },
25432     
25433     getActivePane : function()
25434     {
25435         var r = false;
25436         Roo.each(this.panes, function(p) {
25437             if(p.el.hasClass('active')){
25438                 r = p;
25439                 return false;
25440             }
25441             
25442             return;
25443         });
25444         
25445         return r;
25446     }
25447     
25448     
25449 });
25450
25451  
25452 /*
25453  * - LGPL
25454  *
25455  * Tab pane
25456  * 
25457  */
25458 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25459 /**
25460  * @class Roo.bootstrap.TabPane
25461  * @extends Roo.bootstrap.Component
25462  * Bootstrap TabPane class
25463  * @cfg {Boolean} active (false | true) Default false
25464  * @cfg {String} title title of panel
25465
25466  * 
25467  * @constructor
25468  * Create a new TabPane
25469  * @param {Object} config The config object
25470  */
25471
25472 Roo.bootstrap.dash.TabPane = function(config){
25473     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25474     
25475     this.addEvents({
25476         // raw events
25477         /**
25478          * @event activate
25479          * When a pane is activated
25480          * @param {Roo.bootstrap.dash.TabPane} pane
25481          */
25482         "activate" : true
25483          
25484     });
25485 };
25486
25487 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25488     
25489     active : false,
25490     title : '',
25491     
25492     // the tabBox that this is attached to.
25493     tab : false,
25494      
25495     getAutoCreate : function() 
25496     {
25497         var cfg = {
25498             tag: 'div',
25499             cls: 'tab-pane'
25500         };
25501         
25502         if(this.active){
25503             cfg.cls += ' active';
25504         }
25505         
25506         return cfg;
25507     },
25508     initEvents  : function()
25509     {
25510         //Roo.log('trigger add pane handler');
25511         this.parent().fireEvent('addpane', this)
25512     },
25513     
25514      /**
25515      * Updates the tab title 
25516      * @param {String} html to set the title to.
25517      */
25518     setTitle: function(str)
25519     {
25520         if (!this.tab) {
25521             return;
25522         }
25523         this.title = str;
25524         this.tab.select('a', true).first().dom.innerHTML = str;
25525         
25526     }
25527     
25528     
25529     
25530 });
25531
25532  
25533
25534
25535  /*
25536  * - LGPL
25537  *
25538  * menu
25539  * 
25540  */
25541 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25542
25543 /**
25544  * @class Roo.bootstrap.menu.Menu
25545  * @extends Roo.bootstrap.Component
25546  * Bootstrap Menu class - container for Menu
25547  * @cfg {String} html Text of the menu
25548  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25549  * @cfg {String} icon Font awesome icon
25550  * @cfg {String} pos Menu align to (top | bottom) default bottom
25551  * 
25552  * 
25553  * @constructor
25554  * Create a new Menu
25555  * @param {Object} config The config object
25556  */
25557
25558
25559 Roo.bootstrap.menu.Menu = function(config){
25560     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25561     
25562     this.addEvents({
25563         /**
25564          * @event beforeshow
25565          * Fires before this menu is displayed
25566          * @param {Roo.bootstrap.menu.Menu} this
25567          */
25568         beforeshow : true,
25569         /**
25570          * @event beforehide
25571          * Fires before this menu is hidden
25572          * @param {Roo.bootstrap.menu.Menu} this
25573          */
25574         beforehide : true,
25575         /**
25576          * @event show
25577          * Fires after this menu is displayed
25578          * @param {Roo.bootstrap.menu.Menu} this
25579          */
25580         show : true,
25581         /**
25582          * @event hide
25583          * Fires after this menu is hidden
25584          * @param {Roo.bootstrap.menu.Menu} this
25585          */
25586         hide : true,
25587         /**
25588          * @event click
25589          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25590          * @param {Roo.bootstrap.menu.Menu} this
25591          * @param {Roo.EventObject} e
25592          */
25593         click : true
25594     });
25595     
25596 };
25597
25598 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25599     
25600     submenu : false,
25601     html : '',
25602     weight : 'default',
25603     icon : false,
25604     pos : 'bottom',
25605     
25606     
25607     getChildContainer : function() {
25608         if(this.isSubMenu){
25609             return this.el;
25610         }
25611         
25612         return this.el.select('ul.dropdown-menu', true).first();  
25613     },
25614     
25615     getAutoCreate : function()
25616     {
25617         var text = [
25618             {
25619                 tag : 'span',
25620                 cls : 'roo-menu-text',
25621                 html : this.html
25622             }
25623         ];
25624         
25625         if(this.icon){
25626             text.unshift({
25627                 tag : 'i',
25628                 cls : 'fa ' + this.icon
25629             })
25630         }
25631         
25632         
25633         var cfg = {
25634             tag : 'div',
25635             cls : 'btn-group',
25636             cn : [
25637                 {
25638                     tag : 'button',
25639                     cls : 'dropdown-button btn btn-' + this.weight,
25640                     cn : text
25641                 },
25642                 {
25643                     tag : 'button',
25644                     cls : 'dropdown-toggle btn btn-' + this.weight,
25645                     cn : [
25646                         {
25647                             tag : 'span',
25648                             cls : 'caret'
25649                         }
25650                     ]
25651                 },
25652                 {
25653                     tag : 'ul',
25654                     cls : 'dropdown-menu'
25655                 }
25656             ]
25657             
25658         };
25659         
25660         if(this.pos == 'top'){
25661             cfg.cls += ' dropup';
25662         }
25663         
25664         if(this.isSubMenu){
25665             cfg = {
25666                 tag : 'ul',
25667                 cls : 'dropdown-menu'
25668             }
25669         }
25670         
25671         return cfg;
25672     },
25673     
25674     onRender : function(ct, position)
25675     {
25676         this.isSubMenu = ct.hasClass('dropdown-submenu');
25677         
25678         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25679     },
25680     
25681     initEvents : function() 
25682     {
25683         if(this.isSubMenu){
25684             return;
25685         }
25686         
25687         this.hidden = true;
25688         
25689         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25690         this.triggerEl.on('click', this.onTriggerPress, this);
25691         
25692         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25693         this.buttonEl.on('click', this.onClick, this);
25694         
25695     },
25696     
25697     list : function()
25698     {
25699         if(this.isSubMenu){
25700             return this.el;
25701         }
25702         
25703         return this.el.select('ul.dropdown-menu', true).first();
25704     },
25705     
25706     onClick : function(e)
25707     {
25708         this.fireEvent("click", this, e);
25709     },
25710     
25711     onTriggerPress  : function(e)
25712     {   
25713         if (this.isVisible()) {
25714             this.hide();
25715         } else {
25716             this.show();
25717         }
25718     },
25719     
25720     isVisible : function(){
25721         return !this.hidden;
25722     },
25723     
25724     show : function()
25725     {
25726         this.fireEvent("beforeshow", this);
25727         
25728         this.hidden = false;
25729         this.el.addClass('open');
25730         
25731         Roo.get(document).on("mouseup", this.onMouseUp, this);
25732         
25733         this.fireEvent("show", this);
25734         
25735         
25736     },
25737     
25738     hide : function()
25739     {
25740         this.fireEvent("beforehide", this);
25741         
25742         this.hidden = true;
25743         this.el.removeClass('open');
25744         
25745         Roo.get(document).un("mouseup", this.onMouseUp);
25746         
25747         this.fireEvent("hide", this);
25748     },
25749     
25750     onMouseUp : function()
25751     {
25752         this.hide();
25753     }
25754     
25755 });
25756
25757  
25758  /*
25759  * - LGPL
25760  *
25761  * menu item
25762  * 
25763  */
25764 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25765
25766 /**
25767  * @class Roo.bootstrap.menu.Item
25768  * @extends Roo.bootstrap.Component
25769  * Bootstrap MenuItem class
25770  * @cfg {Boolean} submenu (true | false) default false
25771  * @cfg {String} html text of the item
25772  * @cfg {String} href the link
25773  * @cfg {Boolean} disable (true | false) default false
25774  * @cfg {Boolean} preventDefault (true | false) default true
25775  * @cfg {String} icon Font awesome icon
25776  * @cfg {String} pos Submenu align to (left | right) default right 
25777  * 
25778  * 
25779  * @constructor
25780  * Create a new Item
25781  * @param {Object} config The config object
25782  */
25783
25784
25785 Roo.bootstrap.menu.Item = function(config){
25786     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25787     this.addEvents({
25788         /**
25789          * @event mouseover
25790          * Fires when the mouse is hovering over this menu
25791          * @param {Roo.bootstrap.menu.Item} this
25792          * @param {Roo.EventObject} e
25793          */
25794         mouseover : true,
25795         /**
25796          * @event mouseout
25797          * Fires when the mouse exits this menu
25798          * @param {Roo.bootstrap.menu.Item} this
25799          * @param {Roo.EventObject} e
25800          */
25801         mouseout : true,
25802         // raw events
25803         /**
25804          * @event click
25805          * The raw click event for the entire grid.
25806          * @param {Roo.EventObject} e
25807          */
25808         click : true
25809     });
25810 };
25811
25812 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25813     
25814     submenu : false,
25815     href : '',
25816     html : '',
25817     preventDefault: true,
25818     disable : false,
25819     icon : false,
25820     pos : 'right',
25821     
25822     getAutoCreate : function()
25823     {
25824         var text = [
25825             {
25826                 tag : 'span',
25827                 cls : 'roo-menu-item-text',
25828                 html : this.html
25829             }
25830         ];
25831         
25832         if(this.icon){
25833             text.unshift({
25834                 tag : 'i',
25835                 cls : 'fa ' + this.icon
25836             })
25837         }
25838         
25839         var cfg = {
25840             tag : 'li',
25841             cn : [
25842                 {
25843                     tag : 'a',
25844                     href : this.href || '#',
25845                     cn : text
25846                 }
25847             ]
25848         };
25849         
25850         if(this.disable){
25851             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25852         }
25853         
25854         if(this.submenu){
25855             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25856             
25857             if(this.pos == 'left'){
25858                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25859             }
25860         }
25861         
25862         return cfg;
25863     },
25864     
25865     initEvents : function() 
25866     {
25867         this.el.on('mouseover', this.onMouseOver, this);
25868         this.el.on('mouseout', this.onMouseOut, this);
25869         
25870         this.el.select('a', true).first().on('click', this.onClick, this);
25871         
25872     },
25873     
25874     onClick : function(e)
25875     {
25876         if(this.preventDefault){
25877             e.preventDefault();
25878         }
25879         
25880         this.fireEvent("click", this, e);
25881     },
25882     
25883     onMouseOver : function(e)
25884     {
25885         if(this.submenu && this.pos == 'left'){
25886             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25887         }
25888         
25889         this.fireEvent("mouseover", this, e);
25890     },
25891     
25892     onMouseOut : function(e)
25893     {
25894         this.fireEvent("mouseout", this, e);
25895     }
25896 });
25897
25898  
25899
25900  /*
25901  * - LGPL
25902  *
25903  * menu separator
25904  * 
25905  */
25906 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25907
25908 /**
25909  * @class Roo.bootstrap.menu.Separator
25910  * @extends Roo.bootstrap.Component
25911  * Bootstrap Separator class
25912  * 
25913  * @constructor
25914  * Create a new Separator
25915  * @param {Object} config The config object
25916  */
25917
25918
25919 Roo.bootstrap.menu.Separator = function(config){
25920     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25921 };
25922
25923 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25924     
25925     getAutoCreate : function(){
25926         var cfg = {
25927             tag : 'li',
25928             cls: 'divider'
25929         };
25930         
25931         return cfg;
25932     }
25933    
25934 });
25935
25936  
25937
25938  /*
25939  * - LGPL
25940  *
25941  * Tooltip
25942  * 
25943  */
25944
25945 /**
25946  * @class Roo.bootstrap.Tooltip
25947  * Bootstrap Tooltip class
25948  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25949  * to determine which dom element triggers the tooltip.
25950  * 
25951  * It needs to add support for additional attributes like tooltip-position
25952  * 
25953  * @constructor
25954  * Create a new Toolti
25955  * @param {Object} config The config object
25956  */
25957
25958 Roo.bootstrap.Tooltip = function(config){
25959     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25960     
25961     this.alignment = Roo.bootstrap.Tooltip.alignment;
25962     
25963     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25964         this.alignment = config.alignment;
25965     }
25966     
25967 };
25968
25969 Roo.apply(Roo.bootstrap.Tooltip, {
25970     /**
25971      * @function init initialize tooltip monitoring.
25972      * @static
25973      */
25974     currentEl : false,
25975     currentTip : false,
25976     currentRegion : false,
25977     
25978     //  init : delay?
25979     
25980     init : function()
25981     {
25982         Roo.get(document).on('mouseover', this.enter ,this);
25983         Roo.get(document).on('mouseout', this.leave, this);
25984          
25985         
25986         this.currentTip = new Roo.bootstrap.Tooltip();
25987     },
25988     
25989     enter : function(ev)
25990     {
25991         var dom = ev.getTarget();
25992         
25993         //Roo.log(['enter',dom]);
25994         var el = Roo.fly(dom);
25995         if (this.currentEl) {
25996             //Roo.log(dom);
25997             //Roo.log(this.currentEl);
25998             //Roo.log(this.currentEl.contains(dom));
25999             if (this.currentEl == el) {
26000                 return;
26001             }
26002             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26003                 return;
26004             }
26005
26006         }
26007         
26008         if (this.currentTip.el) {
26009             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26010         }    
26011         //Roo.log(ev);
26012         
26013         if(!el || el.dom == document){
26014             return;
26015         }
26016         
26017         var bindEl = el;
26018         
26019         // you can not look for children, as if el is the body.. then everythign is the child..
26020         if (!el.attr('tooltip')) { //
26021             if (!el.select("[tooltip]").elements.length) {
26022                 return;
26023             }
26024             // is the mouse over this child...?
26025             bindEl = el.select("[tooltip]").first();
26026             var xy = ev.getXY();
26027             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26028                 //Roo.log("not in region.");
26029                 return;
26030             }
26031             //Roo.log("child element over..");
26032             
26033         }
26034         this.currentEl = bindEl;
26035         this.currentTip.bind(bindEl);
26036         this.currentRegion = Roo.lib.Region.getRegion(dom);
26037         this.currentTip.enter();
26038         
26039     },
26040     leave : function(ev)
26041     {
26042         var dom = ev.getTarget();
26043         //Roo.log(['leave',dom]);
26044         if (!this.currentEl) {
26045             return;
26046         }
26047         
26048         
26049         if (dom != this.currentEl.dom) {
26050             return;
26051         }
26052         var xy = ev.getXY();
26053         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26054             return;
26055         }
26056         // only activate leave if mouse cursor is outside... bounding box..
26057         
26058         
26059         
26060         
26061         if (this.currentTip) {
26062             this.currentTip.leave();
26063         }
26064         //Roo.log('clear currentEl');
26065         this.currentEl = false;
26066         
26067         
26068     },
26069     alignment : {
26070         'left' : ['r-l', [-2,0], 'right'],
26071         'right' : ['l-r', [2,0], 'left'],
26072         'bottom' : ['t-b', [0,2], 'top'],
26073         'top' : [ 'b-t', [0,-2], 'bottom']
26074     }
26075     
26076 });
26077
26078
26079 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26080     
26081     
26082     bindEl : false,
26083     
26084     delay : null, // can be { show : 300 , hide: 500}
26085     
26086     timeout : null,
26087     
26088     hoverState : null, //???
26089     
26090     placement : 'bottom', 
26091     
26092     alignment : false,
26093     
26094     getAutoCreate : function(){
26095     
26096         var cfg = {
26097            cls : 'tooltip',
26098            role : 'tooltip',
26099            cn : [
26100                 {
26101                     cls : 'tooltip-arrow'
26102                 },
26103                 {
26104                     cls : 'tooltip-inner'
26105                 }
26106            ]
26107         };
26108         
26109         return cfg;
26110     },
26111     bind : function(el)
26112     {
26113         this.bindEl = el;
26114     },
26115       
26116     
26117     enter : function () {
26118        
26119         if (this.timeout != null) {
26120             clearTimeout(this.timeout);
26121         }
26122         
26123         this.hoverState = 'in';
26124          //Roo.log("enter - show");
26125         if (!this.delay || !this.delay.show) {
26126             this.show();
26127             return;
26128         }
26129         var _t = this;
26130         this.timeout = setTimeout(function () {
26131             if (_t.hoverState == 'in') {
26132                 _t.show();
26133             }
26134         }, this.delay.show);
26135     },
26136     leave : function()
26137     {
26138         clearTimeout(this.timeout);
26139     
26140         this.hoverState = 'out';
26141          if (!this.delay || !this.delay.hide) {
26142             this.hide();
26143             return;
26144         }
26145        
26146         var _t = this;
26147         this.timeout = setTimeout(function () {
26148             //Roo.log("leave - timeout");
26149             
26150             if (_t.hoverState == 'out') {
26151                 _t.hide();
26152                 Roo.bootstrap.Tooltip.currentEl = false;
26153             }
26154         }, delay);
26155     },
26156     
26157     show : function (msg)
26158     {
26159         if (!this.el) {
26160             this.render(document.body);
26161         }
26162         // set content.
26163         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26164         
26165         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26166         
26167         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26168         
26169         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26170         
26171         var placement = typeof this.placement == 'function' ?
26172             this.placement.call(this, this.el, on_el) :
26173             this.placement;
26174             
26175         var autoToken = /\s?auto?\s?/i;
26176         var autoPlace = autoToken.test(placement);
26177         if (autoPlace) {
26178             placement = placement.replace(autoToken, '') || 'top';
26179         }
26180         
26181         //this.el.detach()
26182         //this.el.setXY([0,0]);
26183         this.el.show();
26184         //this.el.dom.style.display='block';
26185         
26186         //this.el.appendTo(on_el);
26187         
26188         var p = this.getPosition();
26189         var box = this.el.getBox();
26190         
26191         if (autoPlace) {
26192             // fixme..
26193         }
26194         
26195         var align = this.alignment[placement];
26196         
26197         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26198         
26199         if(placement == 'top' || placement == 'bottom'){
26200             if(xy[0] < 0){
26201                 placement = 'right';
26202             }
26203             
26204             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26205                 placement = 'left';
26206             }
26207             
26208             var scroll = Roo.select('body', true).first().getScroll();
26209             
26210             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26211                 placement = 'top';
26212             }
26213             
26214         }
26215         
26216         this.el.alignTo(this.bindEl, align[0],align[1]);
26217         //var arrow = this.el.select('.arrow',true).first();
26218         //arrow.set(align[2], 
26219         
26220         this.el.addClass(placement);
26221         
26222         this.el.addClass('in fade');
26223         
26224         this.hoverState = null;
26225         
26226         if (this.el.hasClass('fade')) {
26227             // fade it?
26228         }
26229         
26230     },
26231     hide : function()
26232     {
26233          
26234         if (!this.el) {
26235             return;
26236         }
26237         //this.el.setXY([0,0]);
26238         this.el.removeClass('in');
26239         //this.el.hide();
26240         
26241     }
26242     
26243 });
26244  
26245
26246  /*
26247  * - LGPL
26248  *
26249  * Location Picker
26250  * 
26251  */
26252
26253 /**
26254  * @class Roo.bootstrap.LocationPicker
26255  * @extends Roo.bootstrap.Component
26256  * Bootstrap LocationPicker class
26257  * @cfg {Number} latitude Position when init default 0
26258  * @cfg {Number} longitude Position when init default 0
26259  * @cfg {Number} zoom default 15
26260  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26261  * @cfg {Boolean} mapTypeControl default false
26262  * @cfg {Boolean} disableDoubleClickZoom default false
26263  * @cfg {Boolean} scrollwheel default true
26264  * @cfg {Boolean} streetViewControl default false
26265  * @cfg {Number} radius default 0
26266  * @cfg {String} locationName
26267  * @cfg {Boolean} draggable default true
26268  * @cfg {Boolean} enableAutocomplete default false
26269  * @cfg {Boolean} enableReverseGeocode default true
26270  * @cfg {String} markerTitle
26271  * 
26272  * @constructor
26273  * Create a new LocationPicker
26274  * @param {Object} config The config object
26275  */
26276
26277
26278 Roo.bootstrap.LocationPicker = function(config){
26279     
26280     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26281     
26282     this.addEvents({
26283         /**
26284          * @event initial
26285          * Fires when the picker initialized.
26286          * @param {Roo.bootstrap.LocationPicker} this
26287          * @param {Google Location} location
26288          */
26289         initial : true,
26290         /**
26291          * @event positionchanged
26292          * Fires when the picker position changed.
26293          * @param {Roo.bootstrap.LocationPicker} this
26294          * @param {Google Location} location
26295          */
26296         positionchanged : true,
26297         /**
26298          * @event resize
26299          * Fires when the map resize.
26300          * @param {Roo.bootstrap.LocationPicker} this
26301          */
26302         resize : true,
26303         /**
26304          * @event show
26305          * Fires when the map show.
26306          * @param {Roo.bootstrap.LocationPicker} this
26307          */
26308         show : true,
26309         /**
26310          * @event hide
26311          * Fires when the map hide.
26312          * @param {Roo.bootstrap.LocationPicker} this
26313          */
26314         hide : true,
26315         /**
26316          * @event mapClick
26317          * Fires when click the map.
26318          * @param {Roo.bootstrap.LocationPicker} this
26319          * @param {Map event} e
26320          */
26321         mapClick : true,
26322         /**
26323          * @event mapRightClick
26324          * Fires when right click the map.
26325          * @param {Roo.bootstrap.LocationPicker} this
26326          * @param {Map event} e
26327          */
26328         mapRightClick : true,
26329         /**
26330          * @event markerClick
26331          * Fires when click the marker.
26332          * @param {Roo.bootstrap.LocationPicker} this
26333          * @param {Map event} e
26334          */
26335         markerClick : true,
26336         /**
26337          * @event markerRightClick
26338          * Fires when right click the marker.
26339          * @param {Roo.bootstrap.LocationPicker} this
26340          * @param {Map event} e
26341          */
26342         markerRightClick : true,
26343         /**
26344          * @event OverlayViewDraw
26345          * Fires when OverlayView Draw
26346          * @param {Roo.bootstrap.LocationPicker} this
26347          */
26348         OverlayViewDraw : true,
26349         /**
26350          * @event OverlayViewOnAdd
26351          * Fires when OverlayView Draw
26352          * @param {Roo.bootstrap.LocationPicker} this
26353          */
26354         OverlayViewOnAdd : true,
26355         /**
26356          * @event OverlayViewOnRemove
26357          * Fires when OverlayView Draw
26358          * @param {Roo.bootstrap.LocationPicker} this
26359          */
26360         OverlayViewOnRemove : true,
26361         /**
26362          * @event OverlayViewShow
26363          * Fires when OverlayView Draw
26364          * @param {Roo.bootstrap.LocationPicker} this
26365          * @param {Pixel} cpx
26366          */
26367         OverlayViewShow : true,
26368         /**
26369          * @event OverlayViewHide
26370          * Fires when OverlayView Draw
26371          * @param {Roo.bootstrap.LocationPicker} this
26372          */
26373         OverlayViewHide : true,
26374         /**
26375          * @event loadexception
26376          * Fires when load google lib failed.
26377          * @param {Roo.bootstrap.LocationPicker} this
26378          */
26379         loadexception : true
26380     });
26381         
26382 };
26383
26384 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26385     
26386     gMapContext: false,
26387     
26388     latitude: 0,
26389     longitude: 0,
26390     zoom: 15,
26391     mapTypeId: false,
26392     mapTypeControl: false,
26393     disableDoubleClickZoom: false,
26394     scrollwheel: true,
26395     streetViewControl: false,
26396     radius: 0,
26397     locationName: '',
26398     draggable: true,
26399     enableAutocomplete: false,
26400     enableReverseGeocode: true,
26401     markerTitle: '',
26402     
26403     getAutoCreate: function()
26404     {
26405
26406         var cfg = {
26407             tag: 'div',
26408             cls: 'roo-location-picker'
26409         };
26410         
26411         return cfg
26412     },
26413     
26414     initEvents: function(ct, position)
26415     {       
26416         if(!this.el.getWidth() || this.isApplied()){
26417             return;
26418         }
26419         
26420         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26421         
26422         this.initial();
26423     },
26424     
26425     initial: function()
26426     {
26427         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26428             this.fireEvent('loadexception', this);
26429             return;
26430         }
26431         
26432         if(!this.mapTypeId){
26433             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26434         }
26435         
26436         this.gMapContext = this.GMapContext();
26437         
26438         this.initOverlayView();
26439         
26440         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26441         
26442         var _this = this;
26443                 
26444         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26445             _this.setPosition(_this.gMapContext.marker.position);
26446         });
26447         
26448         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26449             _this.fireEvent('mapClick', this, event);
26450             
26451         });
26452
26453         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26454             _this.fireEvent('mapRightClick', this, event);
26455             
26456         });
26457         
26458         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26459             _this.fireEvent('markerClick', this, event);
26460             
26461         });
26462
26463         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26464             _this.fireEvent('markerRightClick', this, event);
26465             
26466         });
26467         
26468         this.setPosition(this.gMapContext.location);
26469         
26470         this.fireEvent('initial', this, this.gMapContext.location);
26471     },
26472     
26473     initOverlayView: function()
26474     {
26475         var _this = this;
26476         
26477         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26478             
26479             draw: function()
26480             {
26481                 _this.fireEvent('OverlayViewDraw', _this);
26482             },
26483             
26484             onAdd: function()
26485             {
26486                 _this.fireEvent('OverlayViewOnAdd', _this);
26487             },
26488             
26489             onRemove: function()
26490             {
26491                 _this.fireEvent('OverlayViewOnRemove', _this);
26492             },
26493             
26494             show: function(cpx)
26495             {
26496                 _this.fireEvent('OverlayViewShow', _this, cpx);
26497             },
26498             
26499             hide: function()
26500             {
26501                 _this.fireEvent('OverlayViewHide', _this);
26502             }
26503             
26504         });
26505     },
26506     
26507     fromLatLngToContainerPixel: function(event)
26508     {
26509         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26510     },
26511     
26512     isApplied: function() 
26513     {
26514         return this.getGmapContext() == false ? false : true;
26515     },
26516     
26517     getGmapContext: function() 
26518     {
26519         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26520     },
26521     
26522     GMapContext: function() 
26523     {
26524         var position = new google.maps.LatLng(this.latitude, this.longitude);
26525         
26526         var _map = new google.maps.Map(this.el.dom, {
26527             center: position,
26528             zoom: this.zoom,
26529             mapTypeId: this.mapTypeId,
26530             mapTypeControl: this.mapTypeControl,
26531             disableDoubleClickZoom: this.disableDoubleClickZoom,
26532             scrollwheel: this.scrollwheel,
26533             streetViewControl: this.streetViewControl,
26534             locationName: this.locationName,
26535             draggable: this.draggable,
26536             enableAutocomplete: this.enableAutocomplete,
26537             enableReverseGeocode: this.enableReverseGeocode
26538         });
26539         
26540         var _marker = new google.maps.Marker({
26541             position: position,
26542             map: _map,
26543             title: this.markerTitle,
26544             draggable: this.draggable
26545         });
26546         
26547         return {
26548             map: _map,
26549             marker: _marker,
26550             circle: null,
26551             location: position,
26552             radius: this.radius,
26553             locationName: this.locationName,
26554             addressComponents: {
26555                 formatted_address: null,
26556                 addressLine1: null,
26557                 addressLine2: null,
26558                 streetName: null,
26559                 streetNumber: null,
26560                 city: null,
26561                 district: null,
26562                 state: null,
26563                 stateOrProvince: null
26564             },
26565             settings: this,
26566             domContainer: this.el.dom,
26567             geodecoder: new google.maps.Geocoder()
26568         };
26569     },
26570     
26571     drawCircle: function(center, radius, options) 
26572     {
26573         if (this.gMapContext.circle != null) {
26574             this.gMapContext.circle.setMap(null);
26575         }
26576         if (radius > 0) {
26577             radius *= 1;
26578             options = Roo.apply({}, options, {
26579                 strokeColor: "#0000FF",
26580                 strokeOpacity: .35,
26581                 strokeWeight: 2,
26582                 fillColor: "#0000FF",
26583                 fillOpacity: .2
26584             });
26585             
26586             options.map = this.gMapContext.map;
26587             options.radius = radius;
26588             options.center = center;
26589             this.gMapContext.circle = new google.maps.Circle(options);
26590             return this.gMapContext.circle;
26591         }
26592         
26593         return null;
26594     },
26595     
26596     setPosition: function(location) 
26597     {
26598         this.gMapContext.location = location;
26599         this.gMapContext.marker.setPosition(location);
26600         this.gMapContext.map.panTo(location);
26601         this.drawCircle(location, this.gMapContext.radius, {});
26602         
26603         var _this = this;
26604         
26605         if (this.gMapContext.settings.enableReverseGeocode) {
26606             this.gMapContext.geodecoder.geocode({
26607                 latLng: this.gMapContext.location
26608             }, function(results, status) {
26609                 
26610                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26611                     _this.gMapContext.locationName = results[0].formatted_address;
26612                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26613                     
26614                     _this.fireEvent('positionchanged', this, location);
26615                 }
26616             });
26617             
26618             return;
26619         }
26620         
26621         this.fireEvent('positionchanged', this, location);
26622     },
26623     
26624     resize: function()
26625     {
26626         google.maps.event.trigger(this.gMapContext.map, "resize");
26627         
26628         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26629         
26630         this.fireEvent('resize', this);
26631     },
26632     
26633     setPositionByLatLng: function(latitude, longitude)
26634     {
26635         this.setPosition(new google.maps.LatLng(latitude, longitude));
26636     },
26637     
26638     getCurrentPosition: function() 
26639     {
26640         return {
26641             latitude: this.gMapContext.location.lat(),
26642             longitude: this.gMapContext.location.lng()
26643         };
26644     },
26645     
26646     getAddressName: function() 
26647     {
26648         return this.gMapContext.locationName;
26649     },
26650     
26651     getAddressComponents: function() 
26652     {
26653         return this.gMapContext.addressComponents;
26654     },
26655     
26656     address_component_from_google_geocode: function(address_components) 
26657     {
26658         var result = {};
26659         
26660         for (var i = 0; i < address_components.length; i++) {
26661             var component = address_components[i];
26662             if (component.types.indexOf("postal_code") >= 0) {
26663                 result.postalCode = component.short_name;
26664             } else if (component.types.indexOf("street_number") >= 0) {
26665                 result.streetNumber = component.short_name;
26666             } else if (component.types.indexOf("route") >= 0) {
26667                 result.streetName = component.short_name;
26668             } else if (component.types.indexOf("neighborhood") >= 0) {
26669                 result.city = component.short_name;
26670             } else if (component.types.indexOf("locality") >= 0) {
26671                 result.city = component.short_name;
26672             } else if (component.types.indexOf("sublocality") >= 0) {
26673                 result.district = component.short_name;
26674             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26675                 result.stateOrProvince = component.short_name;
26676             } else if (component.types.indexOf("country") >= 0) {
26677                 result.country = component.short_name;
26678             }
26679         }
26680         
26681         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26682         result.addressLine2 = "";
26683         return result;
26684     },
26685     
26686     setZoomLevel: function(zoom)
26687     {
26688         this.gMapContext.map.setZoom(zoom);
26689     },
26690     
26691     show: function()
26692     {
26693         if(!this.el){
26694             return;
26695         }
26696         
26697         this.el.show();
26698         
26699         this.resize();
26700         
26701         this.fireEvent('show', this);
26702     },
26703     
26704     hide: function()
26705     {
26706         if(!this.el){
26707             return;
26708         }
26709         
26710         this.el.hide();
26711         
26712         this.fireEvent('hide', this);
26713     }
26714     
26715 });
26716
26717 Roo.apply(Roo.bootstrap.LocationPicker, {
26718     
26719     OverlayView : function(map, options)
26720     {
26721         options = options || {};
26722         
26723         this.setMap(map);
26724     }
26725     
26726     
26727 });/*
26728  * - LGPL
26729  *
26730  * Alert
26731  * 
26732  */
26733
26734 /**
26735  * @class Roo.bootstrap.Alert
26736  * @extends Roo.bootstrap.Component
26737  * Bootstrap Alert class
26738  * @cfg {String} title The title of alert
26739  * @cfg {String} html The content of alert
26740  * @cfg {String} weight (  success | info | warning | danger )
26741  * @cfg {String} faicon font-awesomeicon
26742  * 
26743  * @constructor
26744  * Create a new alert
26745  * @param {Object} config The config object
26746  */
26747
26748
26749 Roo.bootstrap.Alert = function(config){
26750     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26751     
26752 };
26753
26754 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26755     
26756     title: '',
26757     html: '',
26758     weight: false,
26759     faicon: false,
26760     
26761     getAutoCreate : function()
26762     {
26763         
26764         var cfg = {
26765             tag : 'div',
26766             cls : 'alert',
26767             cn : [
26768                 {
26769                     tag : 'i',
26770                     cls : 'roo-alert-icon'
26771                     
26772                 },
26773                 {
26774                     tag : 'b',
26775                     cls : 'roo-alert-title',
26776                     html : this.title
26777                 },
26778                 {
26779                     tag : 'span',
26780                     cls : 'roo-alert-text',
26781                     html : this.html
26782                 }
26783             ]
26784         };
26785         
26786         if(this.faicon){
26787             cfg.cn[0].cls += ' fa ' + this.faicon;
26788         }
26789         
26790         if(this.weight){
26791             cfg.cls += ' alert-' + this.weight;
26792         }
26793         
26794         return cfg;
26795     },
26796     
26797     initEvents: function() 
26798     {
26799         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26800     },
26801     
26802     setTitle : function(str)
26803     {
26804         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26805     },
26806     
26807     setText : function(str)
26808     {
26809         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26810     },
26811     
26812     setWeight : function(weight)
26813     {
26814         if(this.weight){
26815             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26816         }
26817         
26818         this.weight = weight;
26819         
26820         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26821     },
26822     
26823     setIcon : function(icon)
26824     {
26825         if(this.faicon){
26826             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26827         }
26828         
26829         this.faicon = icon;
26830         
26831         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26832     },
26833     
26834     hide: function() 
26835     {
26836         this.el.hide();   
26837     },
26838     
26839     show: function() 
26840     {  
26841         this.el.show();   
26842     }
26843     
26844 });
26845
26846  
26847 /*
26848 * Licence: LGPL
26849 */
26850
26851 /**
26852  * @class Roo.bootstrap.UploadCropbox
26853  * @extends Roo.bootstrap.Component
26854  * Bootstrap UploadCropbox class
26855  * @cfg {String} emptyText show when image has been loaded
26856  * @cfg {String} rotateNotify show when image too small to rotate
26857  * @cfg {Number} errorTimeout default 3000
26858  * @cfg {Number} minWidth default 300
26859  * @cfg {Number} minHeight default 300
26860  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26861  * @cfg {Boolean} isDocument (true|false) default false
26862  * @cfg {String} url action url
26863  * @cfg {String} paramName default 'imageUpload'
26864  * @cfg {String} method default POST
26865  * @cfg {Boolean} loadMask (true|false) default true
26866  * @cfg {Boolean} loadingText default 'Loading...'
26867  * 
26868  * @constructor
26869  * Create a new UploadCropbox
26870  * @param {Object} config The config object
26871  */
26872
26873 Roo.bootstrap.UploadCropbox = function(config){
26874     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26875     
26876     this.addEvents({
26877         /**
26878          * @event beforeselectfile
26879          * Fire before select file
26880          * @param {Roo.bootstrap.UploadCropbox} this
26881          */
26882         "beforeselectfile" : true,
26883         /**
26884          * @event initial
26885          * Fire after initEvent
26886          * @param {Roo.bootstrap.UploadCropbox} this
26887          */
26888         "initial" : true,
26889         /**
26890          * @event crop
26891          * Fire after initEvent
26892          * @param {Roo.bootstrap.UploadCropbox} this
26893          * @param {String} data
26894          */
26895         "crop" : true,
26896         /**
26897          * @event prepare
26898          * Fire when preparing the file data
26899          * @param {Roo.bootstrap.UploadCropbox} this
26900          * @param {Object} file
26901          */
26902         "prepare" : true,
26903         /**
26904          * @event exception
26905          * Fire when get exception
26906          * @param {Roo.bootstrap.UploadCropbox} this
26907          * @param {XMLHttpRequest} xhr
26908          */
26909         "exception" : true,
26910         /**
26911          * @event beforeloadcanvas
26912          * Fire before load the canvas
26913          * @param {Roo.bootstrap.UploadCropbox} this
26914          * @param {String} src
26915          */
26916         "beforeloadcanvas" : true,
26917         /**
26918          * @event trash
26919          * Fire when trash image
26920          * @param {Roo.bootstrap.UploadCropbox} this
26921          */
26922         "trash" : true,
26923         /**
26924          * @event download
26925          * Fire when download the image
26926          * @param {Roo.bootstrap.UploadCropbox} this
26927          */
26928         "download" : true,
26929         /**
26930          * @event footerbuttonclick
26931          * Fire when footerbuttonclick
26932          * @param {Roo.bootstrap.UploadCropbox} this
26933          * @param {String} type
26934          */
26935         "footerbuttonclick" : true,
26936         /**
26937          * @event resize
26938          * Fire when resize
26939          * @param {Roo.bootstrap.UploadCropbox} this
26940          */
26941         "resize" : true,
26942         /**
26943          * @event rotate
26944          * Fire when rotate the image
26945          * @param {Roo.bootstrap.UploadCropbox} this
26946          * @param {String} pos
26947          */
26948         "rotate" : true,
26949         /**
26950          * @event inspect
26951          * Fire when inspect the file
26952          * @param {Roo.bootstrap.UploadCropbox} this
26953          * @param {Object} file
26954          */
26955         "inspect" : true,
26956         /**
26957          * @event upload
26958          * Fire when xhr upload the file
26959          * @param {Roo.bootstrap.UploadCropbox} this
26960          * @param {Object} data
26961          */
26962         "upload" : true,
26963         /**
26964          * @event arrange
26965          * Fire when arrange the file data
26966          * @param {Roo.bootstrap.UploadCropbox} this
26967          * @param {Object} formData
26968          */
26969         "arrange" : true
26970     });
26971     
26972     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26973 };
26974
26975 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26976     
26977     emptyText : 'Click to upload image',
26978     rotateNotify : 'Image is too small to rotate',
26979     errorTimeout : 3000,
26980     scale : 0,
26981     baseScale : 1,
26982     rotate : 0,
26983     dragable : false,
26984     pinching : false,
26985     mouseX : 0,
26986     mouseY : 0,
26987     cropData : false,
26988     minWidth : 300,
26989     minHeight : 300,
26990     file : false,
26991     exif : {},
26992     baseRotate : 1,
26993     cropType : 'image/jpeg',
26994     buttons : false,
26995     canvasLoaded : false,
26996     isDocument : false,
26997     method : 'POST',
26998     paramName : 'imageUpload',
26999     loadMask : true,
27000     loadingText : 'Loading...',
27001     maskEl : false,
27002     
27003     getAutoCreate : function()
27004     {
27005         var cfg = {
27006             tag : 'div',
27007             cls : 'roo-upload-cropbox',
27008             cn : [
27009                 {
27010                     tag : 'input',
27011                     cls : 'roo-upload-cropbox-selector',
27012                     type : 'file'
27013                 },
27014                 {
27015                     tag : 'div',
27016                     cls : 'roo-upload-cropbox-body',
27017                     style : 'cursor:pointer',
27018                     cn : [
27019                         {
27020                             tag : 'div',
27021                             cls : 'roo-upload-cropbox-preview'
27022                         },
27023                         {
27024                             tag : 'div',
27025                             cls : 'roo-upload-cropbox-thumb'
27026                         },
27027                         {
27028                             tag : 'div',
27029                             cls : 'roo-upload-cropbox-empty-notify',
27030                             html : this.emptyText
27031                         },
27032                         {
27033                             tag : 'div',
27034                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27035                             html : this.rotateNotify
27036                         }
27037                     ]
27038                 },
27039                 {
27040                     tag : 'div',
27041                     cls : 'roo-upload-cropbox-footer',
27042                     cn : {
27043                         tag : 'div',
27044                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27045                         cn : []
27046                     }
27047                 }
27048             ]
27049         };
27050         
27051         return cfg;
27052     },
27053     
27054     onRender : function(ct, position)
27055     {
27056         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27057         
27058         if (this.buttons.length) {
27059             
27060             Roo.each(this.buttons, function(bb) {
27061                 
27062                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27063                 
27064                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27065                 
27066             }, this);
27067         }
27068         
27069         if(this.loadMask){
27070             this.maskEl = this.el;
27071         }
27072     },
27073     
27074     initEvents : function()
27075     {
27076         this.urlAPI = (window.createObjectURL && window) || 
27077                                 (window.URL && URL.revokeObjectURL && URL) || 
27078                                 (window.webkitURL && webkitURL);
27079                         
27080         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27081         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27082         
27083         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27084         this.selectorEl.hide();
27085         
27086         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27087         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27088         
27089         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27090         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27091         this.thumbEl.hide();
27092         
27093         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27094         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27095         
27096         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27097         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27098         this.errorEl.hide();
27099         
27100         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27101         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27102         this.footerEl.hide();
27103         
27104         this.setThumbBoxSize();
27105         
27106         this.bind();
27107         
27108         this.resize();
27109         
27110         this.fireEvent('initial', this);
27111     },
27112
27113     bind : function()
27114     {
27115         var _this = this;
27116         
27117         window.addEventListener("resize", function() { _this.resize(); } );
27118         
27119         this.bodyEl.on('click', this.beforeSelectFile, this);
27120         
27121         if(Roo.isTouch){
27122             this.bodyEl.on('touchstart', this.onTouchStart, this);
27123             this.bodyEl.on('touchmove', this.onTouchMove, this);
27124             this.bodyEl.on('touchend', this.onTouchEnd, this);
27125         }
27126         
27127         if(!Roo.isTouch){
27128             this.bodyEl.on('mousedown', this.onMouseDown, this);
27129             this.bodyEl.on('mousemove', this.onMouseMove, this);
27130             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27131             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27132             Roo.get(document).on('mouseup', this.onMouseUp, this);
27133         }
27134         
27135         this.selectorEl.on('change', this.onFileSelected, this);
27136     },
27137     
27138     reset : function()
27139     {    
27140         this.scale = 0;
27141         this.baseScale = 1;
27142         this.rotate = 0;
27143         this.baseRotate = 1;
27144         this.dragable = false;
27145         this.pinching = false;
27146         this.mouseX = 0;
27147         this.mouseY = 0;
27148         this.cropData = false;
27149         this.notifyEl.dom.innerHTML = this.emptyText;
27150         
27151         this.selectorEl.dom.value = '';
27152         
27153     },
27154     
27155     resize : function()
27156     {
27157         if(this.fireEvent('resize', this) != false){
27158             this.setThumbBoxPosition();
27159             this.setCanvasPosition();
27160         }
27161     },
27162     
27163     onFooterButtonClick : function(e, el, o, type)
27164     {
27165         switch (type) {
27166             case 'rotate-left' :
27167                 this.onRotateLeft(e);
27168                 break;
27169             case 'rotate-right' :
27170                 this.onRotateRight(e);
27171                 break;
27172             case 'picture' :
27173                 this.beforeSelectFile(e);
27174                 break;
27175             case 'trash' :
27176                 this.trash(e);
27177                 break;
27178             case 'crop' :
27179                 this.crop(e);
27180                 break;
27181             case 'download' :
27182                 this.download(e);
27183                 break;
27184             default :
27185                 break;
27186         }
27187         
27188         this.fireEvent('footerbuttonclick', this, type);
27189     },
27190     
27191     beforeSelectFile : function(e)
27192     {
27193         e.preventDefault();
27194         
27195         if(this.fireEvent('beforeselectfile', this) != false){
27196             this.selectorEl.dom.click();
27197         }
27198     },
27199     
27200     onFileSelected : function(e)
27201     {
27202         e.preventDefault();
27203         
27204         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27205             return;
27206         }
27207         
27208         var file = this.selectorEl.dom.files[0];
27209         
27210         if(this.fireEvent('inspect', this, file) != false){
27211             this.prepare(file);
27212         }
27213         
27214     },
27215     
27216     trash : function(e)
27217     {
27218         this.fireEvent('trash', this);
27219     },
27220     
27221     download : function(e)
27222     {
27223         this.fireEvent('download', this);
27224     },
27225     
27226     loadCanvas : function(src)
27227     {   
27228         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27229             
27230             this.reset();
27231             
27232             this.imageEl = document.createElement('img');
27233             
27234             var _this = this;
27235             
27236             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27237             
27238             this.imageEl.src = src;
27239         }
27240     },
27241     
27242     onLoadCanvas : function()
27243     {   
27244         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27245         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27246         
27247         this.bodyEl.un('click', this.beforeSelectFile, this);
27248         
27249         this.notifyEl.hide();
27250         this.thumbEl.show();
27251         this.footerEl.show();
27252         
27253         this.baseRotateLevel();
27254         
27255         if(this.isDocument){
27256             this.setThumbBoxSize();
27257         }
27258         
27259         this.setThumbBoxPosition();
27260         
27261         this.baseScaleLevel();
27262         
27263         this.draw();
27264         
27265         this.resize();
27266         
27267         this.canvasLoaded = true;
27268         
27269         if(this.loadMask){
27270             this.maskEl.unmask();
27271         }
27272         
27273     },
27274     
27275     setCanvasPosition : function()
27276     {   
27277         if(!this.canvasEl){
27278             return;
27279         }
27280         
27281         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27282         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27283         
27284         this.previewEl.setLeft(pw);
27285         this.previewEl.setTop(ph);
27286         
27287     },
27288     
27289     onMouseDown : function(e)
27290     {   
27291         e.stopEvent();
27292         
27293         this.dragable = true;
27294         this.pinching = false;
27295         
27296         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27297             this.dragable = false;
27298             return;
27299         }
27300         
27301         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27302         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27303         
27304     },
27305     
27306     onMouseMove : function(e)
27307     {   
27308         e.stopEvent();
27309         
27310         if(!this.canvasLoaded){
27311             return;
27312         }
27313         
27314         if (!this.dragable){
27315             return;
27316         }
27317         
27318         var minX = Math.ceil(this.thumbEl.getLeft(true));
27319         var minY = Math.ceil(this.thumbEl.getTop(true));
27320         
27321         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27322         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27323         
27324         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27325         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27326         
27327         x = x - this.mouseX;
27328         y = y - this.mouseY;
27329         
27330         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27331         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27332         
27333         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27334         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27335         
27336         this.previewEl.setLeft(bgX);
27337         this.previewEl.setTop(bgY);
27338         
27339         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27340         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27341     },
27342     
27343     onMouseUp : function(e)
27344     {   
27345         e.stopEvent();
27346         
27347         this.dragable = false;
27348     },
27349     
27350     onMouseWheel : function(e)
27351     {   
27352         e.stopEvent();
27353         
27354         this.startScale = this.scale;
27355         
27356         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27357         
27358         if(!this.zoomable()){
27359             this.scale = this.startScale;
27360             return;
27361         }
27362         
27363         this.draw();
27364         
27365         return;
27366     },
27367     
27368     zoomable : function()
27369     {
27370         var minScale = this.thumbEl.getWidth() / this.minWidth;
27371         
27372         if(this.minWidth < this.minHeight){
27373             minScale = this.thumbEl.getHeight() / this.minHeight;
27374         }
27375         
27376         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27377         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27378         
27379         if(
27380                 this.isDocument &&
27381                 (this.rotate == 0 || this.rotate == 180) && 
27382                 (
27383                     width > this.imageEl.OriginWidth || 
27384                     height > this.imageEl.OriginHeight ||
27385                     (width < this.minWidth && height < this.minHeight)
27386                 )
27387         ){
27388             return false;
27389         }
27390         
27391         if(
27392                 this.isDocument &&
27393                 (this.rotate == 90 || this.rotate == 270) && 
27394                 (
27395                     width > this.imageEl.OriginWidth || 
27396                     height > this.imageEl.OriginHeight ||
27397                     (width < this.minHeight && height < this.minWidth)
27398                 )
27399         ){
27400             return false;
27401         }
27402         
27403         if(
27404                 !this.isDocument &&
27405                 (this.rotate == 0 || this.rotate == 180) && 
27406                 (
27407                     width < this.minWidth || 
27408                     width > this.imageEl.OriginWidth || 
27409                     height < this.minHeight || 
27410                     height > this.imageEl.OriginHeight
27411                 )
27412         ){
27413             return false;
27414         }
27415         
27416         if(
27417                 !this.isDocument &&
27418                 (this.rotate == 90 || this.rotate == 270) && 
27419                 (
27420                     width < this.minHeight || 
27421                     width > this.imageEl.OriginWidth || 
27422                     height < this.minWidth || 
27423                     height > this.imageEl.OriginHeight
27424                 )
27425         ){
27426             return false;
27427         }
27428         
27429         return true;
27430         
27431     },
27432     
27433     onRotateLeft : function(e)
27434     {   
27435         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27436             
27437             var minScale = this.thumbEl.getWidth() / this.minWidth;
27438             
27439             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27440             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27441             
27442             this.startScale = this.scale;
27443             
27444             while (this.getScaleLevel() < minScale){
27445             
27446                 this.scale = this.scale + 1;
27447                 
27448                 if(!this.zoomable()){
27449                     break;
27450                 }
27451                 
27452                 if(
27453                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27454                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27455                 ){
27456                     continue;
27457                 }
27458                 
27459                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27460
27461                 this.draw();
27462                 
27463                 return;
27464             }
27465             
27466             this.scale = this.startScale;
27467             
27468             this.onRotateFail();
27469             
27470             return false;
27471         }
27472         
27473         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27474
27475         if(this.isDocument){
27476             this.setThumbBoxSize();
27477             this.setThumbBoxPosition();
27478             this.setCanvasPosition();
27479         }
27480         
27481         this.draw();
27482         
27483         this.fireEvent('rotate', this, 'left');
27484         
27485     },
27486     
27487     onRotateRight : function(e)
27488     {
27489         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27490             
27491             var minScale = this.thumbEl.getWidth() / this.minWidth;
27492         
27493             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27494             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27495             
27496             this.startScale = this.scale;
27497             
27498             while (this.getScaleLevel() < minScale){
27499             
27500                 this.scale = this.scale + 1;
27501                 
27502                 if(!this.zoomable()){
27503                     break;
27504                 }
27505                 
27506                 if(
27507                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27508                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27509                 ){
27510                     continue;
27511                 }
27512                 
27513                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27514
27515                 this.draw();
27516                 
27517                 return;
27518             }
27519             
27520             this.scale = this.startScale;
27521             
27522             this.onRotateFail();
27523             
27524             return false;
27525         }
27526         
27527         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27528
27529         if(this.isDocument){
27530             this.setThumbBoxSize();
27531             this.setThumbBoxPosition();
27532             this.setCanvasPosition();
27533         }
27534         
27535         this.draw();
27536         
27537         this.fireEvent('rotate', this, 'right');
27538     },
27539     
27540     onRotateFail : function()
27541     {
27542         this.errorEl.show(true);
27543         
27544         var _this = this;
27545         
27546         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27547     },
27548     
27549     draw : function()
27550     {
27551         this.previewEl.dom.innerHTML = '';
27552         
27553         var canvasEl = document.createElement("canvas");
27554         
27555         var contextEl = canvasEl.getContext("2d");
27556         
27557         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27558         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27559         var center = this.imageEl.OriginWidth / 2;
27560         
27561         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27562             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27563             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27564             center = this.imageEl.OriginHeight / 2;
27565         }
27566         
27567         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27568         
27569         contextEl.translate(center, center);
27570         contextEl.rotate(this.rotate * Math.PI / 180);
27571
27572         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27573         
27574         this.canvasEl = document.createElement("canvas");
27575         
27576         this.contextEl = this.canvasEl.getContext("2d");
27577         
27578         switch (this.rotate) {
27579             case 0 :
27580                 
27581                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27582                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27583                 
27584                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27585                 
27586                 break;
27587             case 90 : 
27588                 
27589                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27590                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27591                 
27592                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27593                     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);
27594                     break;
27595                 }
27596                 
27597                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27598                 
27599                 break;
27600             case 180 :
27601                 
27602                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27603                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27604                 
27605                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27606                     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);
27607                     break;
27608                 }
27609                 
27610                 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);
27611                 
27612                 break;
27613             case 270 :
27614                 
27615                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27616                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27617         
27618                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27619                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27620                     break;
27621                 }
27622                 
27623                 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);
27624                 
27625                 break;
27626             default : 
27627                 break;
27628         }
27629         
27630         this.previewEl.appendChild(this.canvasEl);
27631         
27632         this.setCanvasPosition();
27633     },
27634     
27635     crop : function()
27636     {
27637         if(!this.canvasLoaded){
27638             return;
27639         }
27640         
27641         var imageCanvas = document.createElement("canvas");
27642         
27643         var imageContext = imageCanvas.getContext("2d");
27644         
27645         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27646         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27647         
27648         var center = imageCanvas.width / 2;
27649         
27650         imageContext.translate(center, center);
27651         
27652         imageContext.rotate(this.rotate * Math.PI / 180);
27653         
27654         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27655         
27656         var canvas = document.createElement("canvas");
27657         
27658         var context = canvas.getContext("2d");
27659                 
27660         canvas.width = this.minWidth;
27661         canvas.height = this.minHeight;
27662
27663         switch (this.rotate) {
27664             case 0 :
27665                 
27666                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27667                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27668                 
27669                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27670                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27671                 
27672                 var targetWidth = this.minWidth - 2 * x;
27673                 var targetHeight = this.minHeight - 2 * y;
27674                 
27675                 var scale = 1;
27676                 
27677                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27678                     scale = targetWidth / width;
27679                 }
27680                 
27681                 if(x > 0 && y == 0){
27682                     scale = targetHeight / height;
27683                 }
27684                 
27685                 if(x > 0 && y > 0){
27686                     scale = targetWidth / width;
27687                     
27688                     if(width < height){
27689                         scale = targetHeight / height;
27690                     }
27691                 }
27692                 
27693                 context.scale(scale, scale);
27694                 
27695                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27696                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27697
27698                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27699                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27700
27701                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27702                 
27703                 break;
27704             case 90 : 
27705                 
27706                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27707                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27708                 
27709                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27710                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27711                 
27712                 var targetWidth = this.minWidth - 2 * x;
27713                 var targetHeight = this.minHeight - 2 * y;
27714                 
27715                 var scale = 1;
27716                 
27717                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27718                     scale = targetWidth / width;
27719                 }
27720                 
27721                 if(x > 0 && y == 0){
27722                     scale = targetHeight / height;
27723                 }
27724                 
27725                 if(x > 0 && y > 0){
27726                     scale = targetWidth / width;
27727                     
27728                     if(width < height){
27729                         scale = targetHeight / height;
27730                     }
27731                 }
27732                 
27733                 context.scale(scale, scale);
27734                 
27735                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27736                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27737
27738                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27739                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27740                 
27741                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27742                 
27743                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27744                 
27745                 break;
27746             case 180 :
27747                 
27748                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27749                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27750                 
27751                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27752                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27753                 
27754                 var targetWidth = this.minWidth - 2 * x;
27755                 var targetHeight = this.minHeight - 2 * y;
27756                 
27757                 var scale = 1;
27758                 
27759                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27760                     scale = targetWidth / width;
27761                 }
27762                 
27763                 if(x > 0 && y == 0){
27764                     scale = targetHeight / height;
27765                 }
27766                 
27767                 if(x > 0 && y > 0){
27768                     scale = targetWidth / width;
27769                     
27770                     if(width < height){
27771                         scale = targetHeight / height;
27772                     }
27773                 }
27774                 
27775                 context.scale(scale, scale);
27776                 
27777                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27778                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27779
27780                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27781                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27782
27783                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27784                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27785                 
27786                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27787                 
27788                 break;
27789             case 270 :
27790                 
27791                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27792                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27793                 
27794                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27795                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27796                 
27797                 var targetWidth = this.minWidth - 2 * x;
27798                 var targetHeight = this.minHeight - 2 * y;
27799                 
27800                 var scale = 1;
27801                 
27802                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27803                     scale = targetWidth / width;
27804                 }
27805                 
27806                 if(x > 0 && y == 0){
27807                     scale = targetHeight / height;
27808                 }
27809                 
27810                 if(x > 0 && y > 0){
27811                     scale = targetWidth / width;
27812                     
27813                     if(width < height){
27814                         scale = targetHeight / height;
27815                     }
27816                 }
27817                 
27818                 context.scale(scale, scale);
27819                 
27820                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27821                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27822
27823                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27824                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27825                 
27826                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27827                 
27828                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27829                 
27830                 break;
27831             default : 
27832                 break;
27833         }
27834         
27835         this.cropData = canvas.toDataURL(this.cropType);
27836         
27837         if(this.fireEvent('crop', this, this.cropData) !== false){
27838             this.process(this.file, this.cropData);
27839         }
27840         
27841         return;
27842         
27843     },
27844     
27845     setThumbBoxSize : function()
27846     {
27847         var width, height;
27848         
27849         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27850             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27851             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27852             
27853             this.minWidth = width;
27854             this.minHeight = height;
27855             
27856             if(this.rotate == 90 || this.rotate == 270){
27857                 this.minWidth = height;
27858                 this.minHeight = width;
27859             }
27860         }
27861         
27862         height = 300;
27863         width = Math.ceil(this.minWidth * height / this.minHeight);
27864         
27865         if(this.minWidth > this.minHeight){
27866             width = 300;
27867             height = Math.ceil(this.minHeight * width / this.minWidth);
27868         }
27869         
27870         this.thumbEl.setStyle({
27871             width : width + 'px',
27872             height : height + 'px'
27873         });
27874
27875         return;
27876             
27877     },
27878     
27879     setThumbBoxPosition : function()
27880     {
27881         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27882         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27883         
27884         this.thumbEl.setLeft(x);
27885         this.thumbEl.setTop(y);
27886         
27887     },
27888     
27889     baseRotateLevel : function()
27890     {
27891         this.baseRotate = 1;
27892         
27893         if(
27894                 typeof(this.exif) != 'undefined' &&
27895                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27896                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27897         ){
27898             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27899         }
27900         
27901         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27902         
27903     },
27904     
27905     baseScaleLevel : function()
27906     {
27907         var width, height;
27908         
27909         if(this.isDocument){
27910             
27911             if(this.baseRotate == 6 || this.baseRotate == 8){
27912             
27913                 height = this.thumbEl.getHeight();
27914                 this.baseScale = height / this.imageEl.OriginWidth;
27915
27916                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27917                     width = this.thumbEl.getWidth();
27918                     this.baseScale = width / this.imageEl.OriginHeight;
27919                 }
27920
27921                 return;
27922             }
27923
27924             height = this.thumbEl.getHeight();
27925             this.baseScale = height / this.imageEl.OriginHeight;
27926
27927             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27928                 width = this.thumbEl.getWidth();
27929                 this.baseScale = width / this.imageEl.OriginWidth;
27930             }
27931
27932             return;
27933         }
27934         
27935         if(this.baseRotate == 6 || this.baseRotate == 8){
27936             
27937             width = this.thumbEl.getHeight();
27938             this.baseScale = width / this.imageEl.OriginHeight;
27939             
27940             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27941                 height = this.thumbEl.getWidth();
27942                 this.baseScale = height / this.imageEl.OriginHeight;
27943             }
27944             
27945             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27946                 height = this.thumbEl.getWidth();
27947                 this.baseScale = height / this.imageEl.OriginHeight;
27948                 
27949                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27950                     width = this.thumbEl.getHeight();
27951                     this.baseScale = width / this.imageEl.OriginWidth;
27952                 }
27953             }
27954             
27955             return;
27956         }
27957         
27958         width = this.thumbEl.getWidth();
27959         this.baseScale = width / this.imageEl.OriginWidth;
27960         
27961         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27962             height = this.thumbEl.getHeight();
27963             this.baseScale = height / this.imageEl.OriginHeight;
27964         }
27965         
27966         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27967             
27968             height = this.thumbEl.getHeight();
27969             this.baseScale = height / this.imageEl.OriginHeight;
27970             
27971             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27972                 width = this.thumbEl.getWidth();
27973                 this.baseScale = width / this.imageEl.OriginWidth;
27974             }
27975             
27976         }
27977         
27978         return;
27979     },
27980     
27981     getScaleLevel : function()
27982     {
27983         return this.baseScale * Math.pow(1.1, this.scale);
27984     },
27985     
27986     onTouchStart : function(e)
27987     {
27988         if(!this.canvasLoaded){
27989             this.beforeSelectFile(e);
27990             return;
27991         }
27992         
27993         var touches = e.browserEvent.touches;
27994         
27995         if(!touches){
27996             return;
27997         }
27998         
27999         if(touches.length == 1){
28000             this.onMouseDown(e);
28001             return;
28002         }
28003         
28004         if(touches.length != 2){
28005             return;
28006         }
28007         
28008         var coords = [];
28009         
28010         for(var i = 0, finger; finger = touches[i]; i++){
28011             coords.push(finger.pageX, finger.pageY);
28012         }
28013         
28014         var x = Math.pow(coords[0] - coords[2], 2);
28015         var y = Math.pow(coords[1] - coords[3], 2);
28016         
28017         this.startDistance = Math.sqrt(x + y);
28018         
28019         this.startScale = this.scale;
28020         
28021         this.pinching = true;
28022         this.dragable = false;
28023         
28024     },
28025     
28026     onTouchMove : function(e)
28027     {
28028         if(!this.pinching && !this.dragable){
28029             return;
28030         }
28031         
28032         var touches = e.browserEvent.touches;
28033         
28034         if(!touches){
28035             return;
28036         }
28037         
28038         if(this.dragable){
28039             this.onMouseMove(e);
28040             return;
28041         }
28042         
28043         var coords = [];
28044         
28045         for(var i = 0, finger; finger = touches[i]; i++){
28046             coords.push(finger.pageX, finger.pageY);
28047         }
28048         
28049         var x = Math.pow(coords[0] - coords[2], 2);
28050         var y = Math.pow(coords[1] - coords[3], 2);
28051         
28052         this.endDistance = Math.sqrt(x + y);
28053         
28054         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28055         
28056         if(!this.zoomable()){
28057             this.scale = this.startScale;
28058             return;
28059         }
28060         
28061         this.draw();
28062         
28063     },
28064     
28065     onTouchEnd : function(e)
28066     {
28067         this.pinching = false;
28068         this.dragable = false;
28069         
28070     },
28071     
28072     process : function(file, crop)
28073     {
28074         if(this.loadMask){
28075             this.maskEl.mask(this.loadingText);
28076         }
28077         
28078         this.xhr = new XMLHttpRequest();
28079         
28080         file.xhr = this.xhr;
28081
28082         this.xhr.open(this.method, this.url, true);
28083         
28084         var headers = {
28085             "Accept": "application/json",
28086             "Cache-Control": "no-cache",
28087             "X-Requested-With": "XMLHttpRequest"
28088         };
28089         
28090         for (var headerName in headers) {
28091             var headerValue = headers[headerName];
28092             if (headerValue) {
28093                 this.xhr.setRequestHeader(headerName, headerValue);
28094             }
28095         }
28096         
28097         var _this = this;
28098         
28099         this.xhr.onload = function()
28100         {
28101             _this.xhrOnLoad(_this.xhr);
28102         }
28103         
28104         this.xhr.onerror = function()
28105         {
28106             _this.xhrOnError(_this.xhr);
28107         }
28108         
28109         var formData = new FormData();
28110
28111         formData.append('returnHTML', 'NO');
28112         
28113         if(crop){
28114             formData.append('crop', crop);
28115         }
28116         
28117         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28118             formData.append(this.paramName, file, file.name);
28119         }
28120         
28121         if(typeof(file.filename) != 'undefined'){
28122             formData.append('filename', file.filename);
28123         }
28124         
28125         if(typeof(file.mimetype) != 'undefined'){
28126             formData.append('mimetype', file.mimetype);
28127         }
28128         
28129         if(this.fireEvent('arrange', this, formData) != false){
28130             this.xhr.send(formData);
28131         };
28132     },
28133     
28134     xhrOnLoad : function(xhr)
28135     {
28136         if(this.loadMask){
28137             this.maskEl.unmask();
28138         }
28139         
28140         if (xhr.readyState !== 4) {
28141             this.fireEvent('exception', this, xhr);
28142             return;
28143         }
28144
28145         var response = Roo.decode(xhr.responseText);
28146         
28147         if(!response.success){
28148             this.fireEvent('exception', this, xhr);
28149             return;
28150         }
28151         
28152         var response = Roo.decode(xhr.responseText);
28153         
28154         this.fireEvent('upload', this, response);
28155         
28156     },
28157     
28158     xhrOnError : function()
28159     {
28160         if(this.loadMask){
28161             this.maskEl.unmask();
28162         }
28163         
28164         Roo.log('xhr on error');
28165         
28166         var response = Roo.decode(xhr.responseText);
28167           
28168         Roo.log(response);
28169         
28170     },
28171     
28172     prepare : function(file)
28173     {   
28174         if(this.loadMask){
28175             this.maskEl.mask(this.loadingText);
28176         }
28177         
28178         this.file = false;
28179         this.exif = {};
28180         
28181         if(typeof(file) === 'string'){
28182             this.loadCanvas(file);
28183             return;
28184         }
28185         
28186         if(!file || !this.urlAPI){
28187             return;
28188         }
28189         
28190         this.file = file;
28191         this.cropType = file.type;
28192         
28193         var _this = this;
28194         
28195         if(this.fireEvent('prepare', this, this.file) != false){
28196             
28197             var reader = new FileReader();
28198             
28199             reader.onload = function (e) {
28200                 if (e.target.error) {
28201                     Roo.log(e.target.error);
28202                     return;
28203                 }
28204                 
28205                 var buffer = e.target.result,
28206                     dataView = new DataView(buffer),
28207                     offset = 2,
28208                     maxOffset = dataView.byteLength - 4,
28209                     markerBytes,
28210                     markerLength;
28211                 
28212                 if (dataView.getUint16(0) === 0xffd8) {
28213                     while (offset < maxOffset) {
28214                         markerBytes = dataView.getUint16(offset);
28215                         
28216                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28217                             markerLength = dataView.getUint16(offset + 2) + 2;
28218                             if (offset + markerLength > dataView.byteLength) {
28219                                 Roo.log('Invalid meta data: Invalid segment size.');
28220                                 break;
28221                             }
28222                             
28223                             if(markerBytes == 0xffe1){
28224                                 _this.parseExifData(
28225                                     dataView,
28226                                     offset,
28227                                     markerLength
28228                                 );
28229                             }
28230                             
28231                             offset += markerLength;
28232                             
28233                             continue;
28234                         }
28235                         
28236                         break;
28237                     }
28238                     
28239                 }
28240                 
28241                 var url = _this.urlAPI.createObjectURL(_this.file);
28242                 
28243                 _this.loadCanvas(url);
28244                 
28245                 return;
28246             }
28247             
28248             reader.readAsArrayBuffer(this.file);
28249             
28250         }
28251         
28252     },
28253     
28254     parseExifData : function(dataView, offset, length)
28255     {
28256         var tiffOffset = offset + 10,
28257             littleEndian,
28258             dirOffset;
28259     
28260         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28261             // No Exif data, might be XMP data instead
28262             return;
28263         }
28264         
28265         // Check for the ASCII code for "Exif" (0x45786966):
28266         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28267             // No Exif data, might be XMP data instead
28268             return;
28269         }
28270         if (tiffOffset + 8 > dataView.byteLength) {
28271             Roo.log('Invalid Exif data: Invalid segment size.');
28272             return;
28273         }
28274         // Check for the two null bytes:
28275         if (dataView.getUint16(offset + 8) !== 0x0000) {
28276             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28277             return;
28278         }
28279         // Check the byte alignment:
28280         switch (dataView.getUint16(tiffOffset)) {
28281         case 0x4949:
28282             littleEndian = true;
28283             break;
28284         case 0x4D4D:
28285             littleEndian = false;
28286             break;
28287         default:
28288             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28289             return;
28290         }
28291         // Check for the TIFF tag marker (0x002A):
28292         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28293             Roo.log('Invalid Exif data: Missing TIFF marker.');
28294             return;
28295         }
28296         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28297         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28298         
28299         this.parseExifTags(
28300             dataView,
28301             tiffOffset,
28302             tiffOffset + dirOffset,
28303             littleEndian
28304         );
28305     },
28306     
28307     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28308     {
28309         var tagsNumber,
28310             dirEndOffset,
28311             i;
28312         if (dirOffset + 6 > dataView.byteLength) {
28313             Roo.log('Invalid Exif data: Invalid directory offset.');
28314             return;
28315         }
28316         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28317         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28318         if (dirEndOffset + 4 > dataView.byteLength) {
28319             Roo.log('Invalid Exif data: Invalid directory size.');
28320             return;
28321         }
28322         for (i = 0; i < tagsNumber; i += 1) {
28323             this.parseExifTag(
28324                 dataView,
28325                 tiffOffset,
28326                 dirOffset + 2 + 12 * i, // tag offset
28327                 littleEndian
28328             );
28329         }
28330         // Return the offset to the next directory:
28331         return dataView.getUint32(dirEndOffset, littleEndian);
28332     },
28333     
28334     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28335     {
28336         var tag = dataView.getUint16(offset, littleEndian);
28337         
28338         this.exif[tag] = this.getExifValue(
28339             dataView,
28340             tiffOffset,
28341             offset,
28342             dataView.getUint16(offset + 2, littleEndian), // tag type
28343             dataView.getUint32(offset + 4, littleEndian), // tag length
28344             littleEndian
28345         );
28346     },
28347     
28348     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28349     {
28350         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28351             tagSize,
28352             dataOffset,
28353             values,
28354             i,
28355             str,
28356             c;
28357     
28358         if (!tagType) {
28359             Roo.log('Invalid Exif data: Invalid tag type.');
28360             return;
28361         }
28362         
28363         tagSize = tagType.size * length;
28364         // Determine if the value is contained in the dataOffset bytes,
28365         // or if the value at the dataOffset is a pointer to the actual data:
28366         dataOffset = tagSize > 4 ?
28367                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28368         if (dataOffset + tagSize > dataView.byteLength) {
28369             Roo.log('Invalid Exif data: Invalid data offset.');
28370             return;
28371         }
28372         if (length === 1) {
28373             return tagType.getValue(dataView, dataOffset, littleEndian);
28374         }
28375         values = [];
28376         for (i = 0; i < length; i += 1) {
28377             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28378         }
28379         
28380         if (tagType.ascii) {
28381             str = '';
28382             // Concatenate the chars:
28383             for (i = 0; i < values.length; i += 1) {
28384                 c = values[i];
28385                 // Ignore the terminating NULL byte(s):
28386                 if (c === '\u0000') {
28387                     break;
28388                 }
28389                 str += c;
28390             }
28391             return str;
28392         }
28393         return values;
28394     }
28395     
28396 });
28397
28398 Roo.apply(Roo.bootstrap.UploadCropbox, {
28399     tags : {
28400         'Orientation': 0x0112
28401     },
28402     
28403     Orientation: {
28404             1: 0, //'top-left',
28405 //            2: 'top-right',
28406             3: 180, //'bottom-right',
28407 //            4: 'bottom-left',
28408 //            5: 'left-top',
28409             6: 90, //'right-top',
28410 //            7: 'right-bottom',
28411             8: 270 //'left-bottom'
28412     },
28413     
28414     exifTagTypes : {
28415         // byte, 8-bit unsigned int:
28416         1: {
28417             getValue: function (dataView, dataOffset) {
28418                 return dataView.getUint8(dataOffset);
28419             },
28420             size: 1
28421         },
28422         // ascii, 8-bit byte:
28423         2: {
28424             getValue: function (dataView, dataOffset) {
28425                 return String.fromCharCode(dataView.getUint8(dataOffset));
28426             },
28427             size: 1,
28428             ascii: true
28429         },
28430         // short, 16 bit int:
28431         3: {
28432             getValue: function (dataView, dataOffset, littleEndian) {
28433                 return dataView.getUint16(dataOffset, littleEndian);
28434             },
28435             size: 2
28436         },
28437         // long, 32 bit int:
28438         4: {
28439             getValue: function (dataView, dataOffset, littleEndian) {
28440                 return dataView.getUint32(dataOffset, littleEndian);
28441             },
28442             size: 4
28443         },
28444         // rational = two long values, first is numerator, second is denominator:
28445         5: {
28446             getValue: function (dataView, dataOffset, littleEndian) {
28447                 return dataView.getUint32(dataOffset, littleEndian) /
28448                     dataView.getUint32(dataOffset + 4, littleEndian);
28449             },
28450             size: 8
28451         },
28452         // slong, 32 bit signed int:
28453         9: {
28454             getValue: function (dataView, dataOffset, littleEndian) {
28455                 return dataView.getInt32(dataOffset, littleEndian);
28456             },
28457             size: 4
28458         },
28459         // srational, two slongs, first is numerator, second is denominator:
28460         10: {
28461             getValue: function (dataView, dataOffset, littleEndian) {
28462                 return dataView.getInt32(dataOffset, littleEndian) /
28463                     dataView.getInt32(dataOffset + 4, littleEndian);
28464             },
28465             size: 8
28466         }
28467     },
28468     
28469     footer : {
28470         STANDARD : [
28471             {
28472                 tag : 'div',
28473                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28474                 action : 'rotate-left',
28475                 cn : [
28476                     {
28477                         tag : 'button',
28478                         cls : 'btn btn-default',
28479                         html : '<i class="fa fa-undo"></i>'
28480                     }
28481                 ]
28482             },
28483             {
28484                 tag : 'div',
28485                 cls : 'btn-group roo-upload-cropbox-picture',
28486                 action : 'picture',
28487                 cn : [
28488                     {
28489                         tag : 'button',
28490                         cls : 'btn btn-default',
28491                         html : '<i class="fa fa-picture-o"></i>'
28492                     }
28493                 ]
28494             },
28495             {
28496                 tag : 'div',
28497                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28498                 action : 'rotate-right',
28499                 cn : [
28500                     {
28501                         tag : 'button',
28502                         cls : 'btn btn-default',
28503                         html : '<i class="fa fa-repeat"></i>'
28504                     }
28505                 ]
28506             }
28507         ],
28508         DOCUMENT : [
28509             {
28510                 tag : 'div',
28511                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28512                 action : 'rotate-left',
28513                 cn : [
28514                     {
28515                         tag : 'button',
28516                         cls : 'btn btn-default',
28517                         html : '<i class="fa fa-undo"></i>'
28518                     }
28519                 ]
28520             },
28521             {
28522                 tag : 'div',
28523                 cls : 'btn-group roo-upload-cropbox-download',
28524                 action : 'download',
28525                 cn : [
28526                     {
28527                         tag : 'button',
28528                         cls : 'btn btn-default',
28529                         html : '<i class="fa fa-download"></i>'
28530                     }
28531                 ]
28532             },
28533             {
28534                 tag : 'div',
28535                 cls : 'btn-group roo-upload-cropbox-crop',
28536                 action : 'crop',
28537                 cn : [
28538                     {
28539                         tag : 'button',
28540                         cls : 'btn btn-default',
28541                         html : '<i class="fa fa-crop"></i>'
28542                     }
28543                 ]
28544             },
28545             {
28546                 tag : 'div',
28547                 cls : 'btn-group roo-upload-cropbox-trash',
28548                 action : 'trash',
28549                 cn : [
28550                     {
28551                         tag : 'button',
28552                         cls : 'btn btn-default',
28553                         html : '<i class="fa fa-trash"></i>'
28554                     }
28555                 ]
28556             },
28557             {
28558                 tag : 'div',
28559                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28560                 action : 'rotate-right',
28561                 cn : [
28562                     {
28563                         tag : 'button',
28564                         cls : 'btn btn-default',
28565                         html : '<i class="fa fa-repeat"></i>'
28566                     }
28567                 ]
28568             }
28569         ],
28570         ROTATOR : [
28571             {
28572                 tag : 'div',
28573                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28574                 action : 'rotate-left',
28575                 cn : [
28576                     {
28577                         tag : 'button',
28578                         cls : 'btn btn-default',
28579                         html : '<i class="fa fa-undo"></i>'
28580                     }
28581                 ]
28582             },
28583             {
28584                 tag : 'div',
28585                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28586                 action : 'rotate-right',
28587                 cn : [
28588                     {
28589                         tag : 'button',
28590                         cls : 'btn btn-default',
28591                         html : '<i class="fa fa-repeat"></i>'
28592                     }
28593                 ]
28594             }
28595         ]
28596     }
28597 });
28598
28599 /*
28600 * Licence: LGPL
28601 */
28602
28603 /**
28604  * @class Roo.bootstrap.DocumentManager
28605  * @extends Roo.bootstrap.Component
28606  * Bootstrap DocumentManager class
28607  * @cfg {String} paramName default 'imageUpload'
28608  * @cfg {String} toolTipName default 'filename'
28609  * @cfg {String} method default POST
28610  * @cfg {String} url action url
28611  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28612  * @cfg {Boolean} multiple multiple upload default true
28613  * @cfg {Number} thumbSize default 300
28614  * @cfg {String} fieldLabel
28615  * @cfg {Number} labelWidth default 4
28616  * @cfg {String} labelAlign (left|top) default left
28617  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28618 * @cfg {Number} labellg set the width of label (1-12)
28619  * @cfg {Number} labelmd set the width of label (1-12)
28620  * @cfg {Number} labelsm set the width of label (1-12)
28621  * @cfg {Number} labelxs set the width of label (1-12)
28622  * 
28623  * @constructor
28624  * Create a new DocumentManager
28625  * @param {Object} config The config object
28626  */
28627
28628 Roo.bootstrap.DocumentManager = function(config){
28629     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28630     
28631     this.files = [];
28632     this.delegates = [];
28633     
28634     this.addEvents({
28635         /**
28636          * @event initial
28637          * Fire when initial the DocumentManager
28638          * @param {Roo.bootstrap.DocumentManager} this
28639          */
28640         "initial" : true,
28641         /**
28642          * @event inspect
28643          * inspect selected file
28644          * @param {Roo.bootstrap.DocumentManager} this
28645          * @param {File} file
28646          */
28647         "inspect" : true,
28648         /**
28649          * @event exception
28650          * Fire when xhr load exception
28651          * @param {Roo.bootstrap.DocumentManager} this
28652          * @param {XMLHttpRequest} xhr
28653          */
28654         "exception" : true,
28655         /**
28656          * @event afterupload
28657          * Fire when xhr load exception
28658          * @param {Roo.bootstrap.DocumentManager} this
28659          * @param {XMLHttpRequest} xhr
28660          */
28661         "afterupload" : true,
28662         /**
28663          * @event prepare
28664          * prepare the form data
28665          * @param {Roo.bootstrap.DocumentManager} this
28666          * @param {Object} formData
28667          */
28668         "prepare" : true,
28669         /**
28670          * @event remove
28671          * Fire when remove the file
28672          * @param {Roo.bootstrap.DocumentManager} this
28673          * @param {Object} file
28674          */
28675         "remove" : true,
28676         /**
28677          * @event refresh
28678          * Fire after refresh the file
28679          * @param {Roo.bootstrap.DocumentManager} this
28680          */
28681         "refresh" : true,
28682         /**
28683          * @event click
28684          * Fire after click the image
28685          * @param {Roo.bootstrap.DocumentManager} this
28686          * @param {Object} file
28687          */
28688         "click" : true,
28689         /**
28690          * @event edit
28691          * Fire when upload a image and editable set to true
28692          * @param {Roo.bootstrap.DocumentManager} this
28693          * @param {Object} file
28694          */
28695         "edit" : true,
28696         /**
28697          * @event beforeselectfile
28698          * Fire before select file
28699          * @param {Roo.bootstrap.DocumentManager} this
28700          */
28701         "beforeselectfile" : true,
28702         /**
28703          * @event process
28704          * Fire before process file
28705          * @param {Roo.bootstrap.DocumentManager} this
28706          * @param {Object} file
28707          */
28708         "process" : true,
28709         /**
28710          * @event previewrendered
28711          * Fire when preview rendered
28712          * @param {Roo.bootstrap.DocumentManager} this
28713          * @param {Object} file
28714          */
28715         "previewrendered" : true,
28716         /**
28717          */
28718         "previewResize" : true
28719         
28720     });
28721 };
28722
28723 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28724     
28725     boxes : 0,
28726     inputName : '',
28727     thumbSize : 300,
28728     multiple : true,
28729     files : false,
28730     method : 'POST',
28731     url : '',
28732     paramName : 'imageUpload',
28733     toolTipName : 'filename',
28734     fieldLabel : '',
28735     labelWidth : 4,
28736     labelAlign : 'left',
28737     editable : true,
28738     delegates : false,
28739     xhr : false, 
28740     
28741     labellg : 0,
28742     labelmd : 0,
28743     labelsm : 0,
28744     labelxs : 0,
28745     
28746     getAutoCreate : function()
28747     {   
28748         var managerWidget = {
28749             tag : 'div',
28750             cls : 'roo-document-manager',
28751             cn : [
28752                 {
28753                     tag : 'input',
28754                     cls : 'roo-document-manager-selector',
28755                     type : 'file'
28756                 },
28757                 {
28758                     tag : 'div',
28759                     cls : 'roo-document-manager-uploader',
28760                     cn : [
28761                         {
28762                             tag : 'div',
28763                             cls : 'roo-document-manager-upload-btn',
28764                             html : '<i class="fa fa-plus"></i>'
28765                         }
28766                     ]
28767                     
28768                 }
28769             ]
28770         };
28771         
28772         var content = [
28773             {
28774                 tag : 'div',
28775                 cls : 'column col-md-12',
28776                 cn : managerWidget
28777             }
28778         ];
28779         
28780         if(this.fieldLabel.length){
28781             
28782             content = [
28783                 {
28784                     tag : 'div',
28785                     cls : 'column col-md-12',
28786                     html : this.fieldLabel
28787                 },
28788                 {
28789                     tag : 'div',
28790                     cls : 'column col-md-12',
28791                     cn : managerWidget
28792                 }
28793             ];
28794
28795             if(this.labelAlign == 'left'){
28796                 content = [
28797                     {
28798                         tag : 'div',
28799                         cls : 'column',
28800                         html : this.fieldLabel
28801                     },
28802                     {
28803                         tag : 'div',
28804                         cls : 'column',
28805                         cn : managerWidget
28806                     }
28807                 ];
28808                 
28809                 if(this.labelWidth > 12){
28810                     content[0].style = "width: " + this.labelWidth + 'px';
28811                 }
28812
28813                 if(this.labelWidth < 13 && this.labelmd == 0){
28814                     this.labelmd = this.labelWidth;
28815                 }
28816
28817                 if(this.labellg > 0){
28818                     content[0].cls += ' col-lg-' + this.labellg;
28819                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28820                 }
28821
28822                 if(this.labelmd > 0){
28823                     content[0].cls += ' col-md-' + this.labelmd;
28824                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28825                 }
28826
28827                 if(this.labelsm > 0){
28828                     content[0].cls += ' col-sm-' + this.labelsm;
28829                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28830                 }
28831
28832                 if(this.labelxs > 0){
28833                     content[0].cls += ' col-xs-' + this.labelxs;
28834                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28835                 }
28836                 
28837             }
28838         }
28839         
28840         var cfg = {
28841             tag : 'div',
28842             cls : 'row clearfix',
28843             cn : content
28844         };
28845         
28846         return cfg;
28847         
28848     },
28849     
28850     initEvents : function()
28851     {
28852         this.managerEl = this.el.select('.roo-document-manager', true).first();
28853         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28854         
28855         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28856         this.selectorEl.hide();
28857         
28858         if(this.multiple){
28859             this.selectorEl.attr('multiple', 'multiple');
28860         }
28861         
28862         this.selectorEl.on('change', this.onFileSelected, this);
28863         
28864         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28865         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28866         
28867         this.uploader.on('click', this.onUploaderClick, this);
28868         
28869         this.renderProgressDialog();
28870         
28871         var _this = this;
28872         
28873         window.addEventListener("resize", function() { _this.refresh(); } );
28874         
28875         this.fireEvent('initial', this);
28876     },
28877     
28878     renderProgressDialog : function()
28879     {
28880         var _this = this;
28881         
28882         this.progressDialog = new Roo.bootstrap.Modal({
28883             cls : 'roo-document-manager-progress-dialog',
28884             allow_close : false,
28885             title : '',
28886             buttons : [
28887                 {
28888                     name  :'cancel',
28889                     weight : 'danger',
28890                     html : 'Cancel'
28891                 }
28892             ], 
28893             listeners : { 
28894                 btnclick : function() {
28895                     _this.uploadCancel();
28896                     this.hide();
28897                 }
28898             }
28899         });
28900          
28901         this.progressDialog.render(Roo.get(document.body));
28902          
28903         this.progress = new Roo.bootstrap.Progress({
28904             cls : 'roo-document-manager-progress',
28905             active : true,
28906             striped : true
28907         });
28908         
28909         this.progress.render(this.progressDialog.getChildContainer());
28910         
28911         this.progressBar = new Roo.bootstrap.ProgressBar({
28912             cls : 'roo-document-manager-progress-bar',
28913             aria_valuenow : 0,
28914             aria_valuemin : 0,
28915             aria_valuemax : 12,
28916             panel : 'success'
28917         });
28918         
28919         this.progressBar.render(this.progress.getChildContainer());
28920     },
28921     
28922     onUploaderClick : function(e)
28923     {
28924         e.preventDefault();
28925      
28926         if(this.fireEvent('beforeselectfile', this) != false){
28927             this.selectorEl.dom.click();
28928         }
28929         
28930     },
28931     
28932     onFileSelected : function(e)
28933     {
28934         e.preventDefault();
28935         
28936         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28937             return;
28938         }
28939         
28940         Roo.each(this.selectorEl.dom.files, function(file){
28941             if(this.fireEvent('inspect', this, file) != false){
28942                 this.files.push(file);
28943             }
28944         }, this);
28945         
28946         this.queue();
28947         
28948     },
28949     
28950     queue : function()
28951     {
28952         this.selectorEl.dom.value = '';
28953         
28954         if(!this.files || !this.files.length){
28955             return;
28956         }
28957         
28958         if(this.boxes > 0 && this.files.length > this.boxes){
28959             this.files = this.files.slice(0, this.boxes);
28960         }
28961         
28962         this.uploader.show();
28963         
28964         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28965             this.uploader.hide();
28966         }
28967         
28968         var _this = this;
28969         
28970         var files = [];
28971         
28972         var docs = [];
28973         
28974         Roo.each(this.files, function(file){
28975             
28976             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28977                 var f = this.renderPreview(file);
28978                 files.push(f);
28979                 return;
28980             }
28981             
28982             if(file.type.indexOf('image') != -1){
28983                 this.delegates.push(
28984                     (function(){
28985                         _this.process(file);
28986                     }).createDelegate(this)
28987                 );
28988         
28989                 return;
28990             }
28991             
28992             docs.push(
28993                 (function(){
28994                     _this.process(file);
28995                 }).createDelegate(this)
28996             );
28997             
28998         }, this);
28999         
29000         this.files = files;
29001         
29002         this.delegates = this.delegates.concat(docs);
29003         
29004         if(!this.delegates.length){
29005             this.refresh();
29006             return;
29007         }
29008         
29009         this.progressBar.aria_valuemax = this.delegates.length;
29010         
29011         this.arrange();
29012         
29013         return;
29014     },
29015     
29016     arrange : function()
29017     {
29018         if(!this.delegates.length){
29019             this.progressDialog.hide();
29020             this.refresh();
29021             return;
29022         }
29023         
29024         var delegate = this.delegates.shift();
29025         
29026         this.progressDialog.show();
29027         
29028         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29029         
29030         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29031         
29032         delegate();
29033     },
29034     
29035     refresh : function()
29036     {
29037         this.uploader.show();
29038         
29039         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29040             this.uploader.hide();
29041         }
29042         
29043         Roo.isTouch ? this.closable(false) : this.closable(true);
29044         
29045         this.fireEvent('refresh', this);
29046     },
29047     
29048     onRemove : function(e, el, o)
29049     {
29050         e.preventDefault();
29051         
29052         this.fireEvent('remove', this, o);
29053         
29054     },
29055     
29056     remove : function(o)
29057     {
29058         var files = [];
29059         
29060         Roo.each(this.files, function(file){
29061             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29062                 files.push(file);
29063                 return;
29064             }
29065
29066             o.target.remove();
29067
29068         }, this);
29069         
29070         this.files = files;
29071         
29072         this.refresh();
29073     },
29074     
29075     clear : function()
29076     {
29077         Roo.each(this.files, function(file){
29078             if(!file.target){
29079                 return;
29080             }
29081             
29082             file.target.remove();
29083
29084         }, this);
29085         
29086         this.files = [];
29087         
29088         this.refresh();
29089     },
29090     
29091     onClick : function(e, el, o)
29092     {
29093         e.preventDefault();
29094         
29095         this.fireEvent('click', this, o);
29096         
29097     },
29098     
29099     closable : function(closable)
29100     {
29101         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29102             
29103             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29104             
29105             if(closable){
29106                 el.show();
29107                 return;
29108             }
29109             
29110             el.hide();
29111             
29112         }, this);
29113     },
29114     
29115     xhrOnLoad : function(xhr)
29116     {
29117         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29118             el.remove();
29119         }, this);
29120         
29121         if (xhr.readyState !== 4) {
29122             this.arrange();
29123             this.fireEvent('exception', this, xhr);
29124             return;
29125         }
29126
29127         var response = Roo.decode(xhr.responseText);
29128         
29129         if(!response.success){
29130             this.arrange();
29131             this.fireEvent('exception', this, xhr);
29132             return;
29133         }
29134         
29135         var file = this.renderPreview(response.data);
29136         
29137         this.files.push(file);
29138         
29139         this.arrange();
29140         
29141         this.fireEvent('afterupload', this, xhr);
29142         
29143     },
29144     
29145     xhrOnError : function(xhr)
29146     {
29147         Roo.log('xhr on error');
29148         
29149         var response = Roo.decode(xhr.responseText);
29150           
29151         Roo.log(response);
29152         
29153         this.arrange();
29154     },
29155     
29156     process : function(file)
29157     {
29158         if(this.fireEvent('process', this, file) !== false){
29159             if(this.editable && file.type.indexOf('image') != -1){
29160                 this.fireEvent('edit', this, file);
29161                 return;
29162             }
29163
29164             this.uploadStart(file, false);
29165
29166             return;
29167         }
29168         
29169     },
29170     
29171     uploadStart : function(file, crop)
29172     {
29173         this.xhr = new XMLHttpRequest();
29174         
29175         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29176             this.arrange();
29177             return;
29178         }
29179         
29180         file.xhr = this.xhr;
29181             
29182         this.managerEl.createChild({
29183             tag : 'div',
29184             cls : 'roo-document-manager-loading',
29185             cn : [
29186                 {
29187                     tag : 'div',
29188                     tooltip : file.name,
29189                     cls : 'roo-document-manager-thumb',
29190                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29191                 }
29192             ]
29193
29194         });
29195
29196         this.xhr.open(this.method, this.url, true);
29197         
29198         var headers = {
29199             "Accept": "application/json",
29200             "Cache-Control": "no-cache",
29201             "X-Requested-With": "XMLHttpRequest"
29202         };
29203         
29204         for (var headerName in headers) {
29205             var headerValue = headers[headerName];
29206             if (headerValue) {
29207                 this.xhr.setRequestHeader(headerName, headerValue);
29208             }
29209         }
29210         
29211         var _this = this;
29212         
29213         this.xhr.onload = function()
29214         {
29215             _this.xhrOnLoad(_this.xhr);
29216         }
29217         
29218         this.xhr.onerror = function()
29219         {
29220             _this.xhrOnError(_this.xhr);
29221         }
29222         
29223         var formData = new FormData();
29224
29225         formData.append('returnHTML', 'NO');
29226         
29227         if(crop){
29228             formData.append('crop', crop);
29229         }
29230         
29231         formData.append(this.paramName, file, file.name);
29232         
29233         var options = {
29234             file : file, 
29235             manually : false
29236         };
29237         
29238         if(this.fireEvent('prepare', this, formData, options) != false){
29239             
29240             if(options.manually){
29241                 return;
29242             }
29243             
29244             this.xhr.send(formData);
29245             return;
29246         };
29247         
29248         this.uploadCancel();
29249     },
29250     
29251     uploadCancel : function()
29252     {
29253         if (this.xhr) {
29254             this.xhr.abort();
29255         }
29256         
29257         this.delegates = [];
29258         
29259         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29260             el.remove();
29261         }, this);
29262         
29263         this.arrange();
29264     },
29265     
29266     renderPreview : function(file)
29267     {
29268         if(typeof(file.target) != 'undefined' && file.target){
29269             return file;
29270         }
29271         
29272         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29273         
29274         var previewEl = this.managerEl.createChild({
29275             tag : 'div',
29276             cls : 'roo-document-manager-preview',
29277             cn : [
29278                 {
29279                     tag : 'div',
29280                     tooltip : file[this.toolTipName],
29281                     cls : 'roo-document-manager-thumb',
29282                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29283                 },
29284                 {
29285                     tag : 'button',
29286                     cls : 'close',
29287                     html : '<i class="fa fa-times-circle"></i>'
29288                 }
29289             ]
29290         });
29291
29292         var close = previewEl.select('button.close', true).first();
29293
29294         close.on('click', this.onRemove, this, file);
29295
29296         file.target = previewEl;
29297
29298         var image = previewEl.select('img', true).first();
29299         
29300         var _this = this;
29301         
29302         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29303         
29304         image.on('click', this.onClick, this, file);
29305         
29306         this.fireEvent('previewrendered', this, file);
29307         
29308         return file;
29309         
29310     },
29311     
29312     onPreviewLoad : function(file, image)
29313     {
29314         if(typeof(file.target) == 'undefined' || !file.target){
29315             return;
29316         }
29317         
29318         var width = image.dom.naturalWidth || image.dom.width;
29319         var height = image.dom.naturalHeight || image.dom.height;
29320         
29321         if(!this.previewResize) {
29322             return;
29323         }
29324         
29325         if(width > height){
29326             file.target.addClass('wide');
29327             return;
29328         }
29329         
29330         file.target.addClass('tall');
29331         return;
29332         
29333     },
29334     
29335     uploadFromSource : function(file, crop)
29336     {
29337         this.xhr = new XMLHttpRequest();
29338         
29339         this.managerEl.createChild({
29340             tag : 'div',
29341             cls : 'roo-document-manager-loading',
29342             cn : [
29343                 {
29344                     tag : 'div',
29345                     tooltip : file.name,
29346                     cls : 'roo-document-manager-thumb',
29347                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29348                 }
29349             ]
29350
29351         });
29352
29353         this.xhr.open(this.method, this.url, true);
29354         
29355         var headers = {
29356             "Accept": "application/json",
29357             "Cache-Control": "no-cache",
29358             "X-Requested-With": "XMLHttpRequest"
29359         };
29360         
29361         for (var headerName in headers) {
29362             var headerValue = headers[headerName];
29363             if (headerValue) {
29364                 this.xhr.setRequestHeader(headerName, headerValue);
29365             }
29366         }
29367         
29368         var _this = this;
29369         
29370         this.xhr.onload = function()
29371         {
29372             _this.xhrOnLoad(_this.xhr);
29373         }
29374         
29375         this.xhr.onerror = function()
29376         {
29377             _this.xhrOnError(_this.xhr);
29378         }
29379         
29380         var formData = new FormData();
29381
29382         formData.append('returnHTML', 'NO');
29383         
29384         formData.append('crop', crop);
29385         
29386         if(typeof(file.filename) != 'undefined'){
29387             formData.append('filename', file.filename);
29388         }
29389         
29390         if(typeof(file.mimetype) != 'undefined'){
29391             formData.append('mimetype', file.mimetype);
29392         }
29393         
29394         Roo.log(formData);
29395         
29396         if(this.fireEvent('prepare', this, formData) != false){
29397             this.xhr.send(formData);
29398         };
29399     }
29400 });
29401
29402 /*
29403 * Licence: LGPL
29404 */
29405
29406 /**
29407  * @class Roo.bootstrap.DocumentViewer
29408  * @extends Roo.bootstrap.Component
29409  * Bootstrap DocumentViewer class
29410  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29411  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29412  * 
29413  * @constructor
29414  * Create a new DocumentViewer
29415  * @param {Object} config The config object
29416  */
29417
29418 Roo.bootstrap.DocumentViewer = function(config){
29419     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29420     
29421     this.addEvents({
29422         /**
29423          * @event initial
29424          * Fire after initEvent
29425          * @param {Roo.bootstrap.DocumentViewer} this
29426          */
29427         "initial" : true,
29428         /**
29429          * @event click
29430          * Fire after click
29431          * @param {Roo.bootstrap.DocumentViewer} this
29432          */
29433         "click" : true,
29434         /**
29435          * @event download
29436          * Fire after download button
29437          * @param {Roo.bootstrap.DocumentViewer} this
29438          */
29439         "download" : true,
29440         /**
29441          * @event trash
29442          * Fire after trash button
29443          * @param {Roo.bootstrap.DocumentViewer} this
29444          */
29445         "trash" : true
29446         
29447     });
29448 };
29449
29450 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29451     
29452     showDownload : true,
29453     
29454     showTrash : true,
29455     
29456     getAutoCreate : function()
29457     {
29458         var cfg = {
29459             tag : 'div',
29460             cls : 'roo-document-viewer',
29461             cn : [
29462                 {
29463                     tag : 'div',
29464                     cls : 'roo-document-viewer-body',
29465                     cn : [
29466                         {
29467                             tag : 'div',
29468                             cls : 'roo-document-viewer-thumb',
29469                             cn : [
29470                                 {
29471                                     tag : 'img',
29472                                     cls : 'roo-document-viewer-image'
29473                                 }
29474                             ]
29475                         }
29476                     ]
29477                 },
29478                 {
29479                     tag : 'div',
29480                     cls : 'roo-document-viewer-footer',
29481                     cn : {
29482                         tag : 'div',
29483                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29484                         cn : [
29485                             {
29486                                 tag : 'div',
29487                                 cls : 'btn-group roo-document-viewer-download',
29488                                 cn : [
29489                                     {
29490                                         tag : 'button',
29491                                         cls : 'btn btn-default',
29492                                         html : '<i class="fa fa-download"></i>'
29493                                     }
29494                                 ]
29495                             },
29496                             {
29497                                 tag : 'div',
29498                                 cls : 'btn-group roo-document-viewer-trash',
29499                                 cn : [
29500                                     {
29501                                         tag : 'button',
29502                                         cls : 'btn btn-default',
29503                                         html : '<i class="fa fa-trash"></i>'
29504                                     }
29505                                 ]
29506                             }
29507                         ]
29508                     }
29509                 }
29510             ]
29511         };
29512         
29513         return cfg;
29514     },
29515     
29516     initEvents : function()
29517     {
29518         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29519         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29520         
29521         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29522         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29523         
29524         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29525         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29526         
29527         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29528         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29529         
29530         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29531         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29532         
29533         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29534         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29535         
29536         this.bodyEl.on('click', this.onClick, this);
29537         this.downloadBtn.on('click', this.onDownload, this);
29538         this.trashBtn.on('click', this.onTrash, this);
29539         
29540         this.downloadBtn.hide();
29541         this.trashBtn.hide();
29542         
29543         if(this.showDownload){
29544             this.downloadBtn.show();
29545         }
29546         
29547         if(this.showTrash){
29548             this.trashBtn.show();
29549         }
29550         
29551         if(!this.showDownload && !this.showTrash) {
29552             this.footerEl.hide();
29553         }
29554         
29555     },
29556     
29557     initial : function()
29558     {
29559         this.fireEvent('initial', this);
29560         
29561     },
29562     
29563     onClick : function(e)
29564     {
29565         e.preventDefault();
29566         
29567         this.fireEvent('click', this);
29568     },
29569     
29570     onDownload : function(e)
29571     {
29572         e.preventDefault();
29573         
29574         this.fireEvent('download', this);
29575     },
29576     
29577     onTrash : function(e)
29578     {
29579         e.preventDefault();
29580         
29581         this.fireEvent('trash', this);
29582     }
29583     
29584 });
29585 /*
29586  * - LGPL
29587  *
29588  * nav progress bar
29589  * 
29590  */
29591
29592 /**
29593  * @class Roo.bootstrap.NavProgressBar
29594  * @extends Roo.bootstrap.Component
29595  * Bootstrap NavProgressBar class
29596  * 
29597  * @constructor
29598  * Create a new nav progress bar
29599  * @param {Object} config The config object
29600  */
29601
29602 Roo.bootstrap.NavProgressBar = function(config){
29603     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29604
29605     this.bullets = this.bullets || [];
29606    
29607 //    Roo.bootstrap.NavProgressBar.register(this);
29608      this.addEvents({
29609         /**
29610              * @event changed
29611              * Fires when the active item changes
29612              * @param {Roo.bootstrap.NavProgressBar} this
29613              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29614              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29615          */
29616         'changed': true
29617      });
29618     
29619 };
29620
29621 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29622     
29623     bullets : [],
29624     barItems : [],
29625     
29626     getAutoCreate : function()
29627     {
29628         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29629         
29630         cfg = {
29631             tag : 'div',
29632             cls : 'roo-navigation-bar-group',
29633             cn : [
29634                 {
29635                     tag : 'div',
29636                     cls : 'roo-navigation-top-bar'
29637                 },
29638                 {
29639                     tag : 'div',
29640                     cls : 'roo-navigation-bullets-bar',
29641                     cn : [
29642                         {
29643                             tag : 'ul',
29644                             cls : 'roo-navigation-bar'
29645                         }
29646                     ]
29647                 },
29648                 
29649                 {
29650                     tag : 'div',
29651                     cls : 'roo-navigation-bottom-bar'
29652                 }
29653             ]
29654             
29655         };
29656         
29657         return cfg;
29658         
29659     },
29660     
29661     initEvents: function() 
29662     {
29663         
29664     },
29665     
29666     onRender : function(ct, position) 
29667     {
29668         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29669         
29670         if(this.bullets.length){
29671             Roo.each(this.bullets, function(b){
29672                this.addItem(b);
29673             }, this);
29674         }
29675         
29676         this.format();
29677         
29678     },
29679     
29680     addItem : function(cfg)
29681     {
29682         var item = new Roo.bootstrap.NavProgressItem(cfg);
29683         
29684         item.parentId = this.id;
29685         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29686         
29687         if(cfg.html){
29688             var top = new Roo.bootstrap.Element({
29689                 tag : 'div',
29690                 cls : 'roo-navigation-bar-text'
29691             });
29692             
29693             var bottom = new Roo.bootstrap.Element({
29694                 tag : 'div',
29695                 cls : 'roo-navigation-bar-text'
29696             });
29697             
29698             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29699             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29700             
29701             var topText = new Roo.bootstrap.Element({
29702                 tag : 'span',
29703                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29704             });
29705             
29706             var bottomText = new Roo.bootstrap.Element({
29707                 tag : 'span',
29708                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29709             });
29710             
29711             topText.onRender(top.el, null);
29712             bottomText.onRender(bottom.el, null);
29713             
29714             item.topEl = top;
29715             item.bottomEl = bottom;
29716         }
29717         
29718         this.barItems.push(item);
29719         
29720         return item;
29721     },
29722     
29723     getActive : function()
29724     {
29725         var active = false;
29726         
29727         Roo.each(this.barItems, function(v){
29728             
29729             if (!v.isActive()) {
29730                 return;
29731             }
29732             
29733             active = v;
29734             return false;
29735             
29736         });
29737         
29738         return active;
29739     },
29740     
29741     setActiveItem : function(item)
29742     {
29743         var prev = false;
29744         
29745         Roo.each(this.barItems, function(v){
29746             if (v.rid == item.rid) {
29747                 return ;
29748             }
29749             
29750             if (v.isActive()) {
29751                 v.setActive(false);
29752                 prev = v;
29753             }
29754         });
29755
29756         item.setActive(true);
29757         
29758         this.fireEvent('changed', this, item, prev);
29759     },
29760     
29761     getBarItem: function(rid)
29762     {
29763         var ret = false;
29764         
29765         Roo.each(this.barItems, function(e) {
29766             if (e.rid != rid) {
29767                 return;
29768             }
29769             
29770             ret =  e;
29771             return false;
29772         });
29773         
29774         return ret;
29775     },
29776     
29777     indexOfItem : function(item)
29778     {
29779         var index = false;
29780         
29781         Roo.each(this.barItems, function(v, i){
29782             
29783             if (v.rid != item.rid) {
29784                 return;
29785             }
29786             
29787             index = i;
29788             return false
29789         });
29790         
29791         return index;
29792     },
29793     
29794     setActiveNext : function()
29795     {
29796         var i = this.indexOfItem(this.getActive());
29797         
29798         if (i > this.barItems.length) {
29799             return;
29800         }
29801         
29802         this.setActiveItem(this.barItems[i+1]);
29803     },
29804     
29805     setActivePrev : function()
29806     {
29807         var i = this.indexOfItem(this.getActive());
29808         
29809         if (i  < 1) {
29810             return;
29811         }
29812         
29813         this.setActiveItem(this.barItems[i-1]);
29814     },
29815     
29816     format : function()
29817     {
29818         if(!this.barItems.length){
29819             return;
29820         }
29821      
29822         var width = 100 / this.barItems.length;
29823         
29824         Roo.each(this.barItems, function(i){
29825             i.el.setStyle('width', width + '%');
29826             i.topEl.el.setStyle('width', width + '%');
29827             i.bottomEl.el.setStyle('width', width + '%');
29828         }, this);
29829         
29830     }
29831     
29832 });
29833 /*
29834  * - LGPL
29835  *
29836  * Nav Progress Item
29837  * 
29838  */
29839
29840 /**
29841  * @class Roo.bootstrap.NavProgressItem
29842  * @extends Roo.bootstrap.Component
29843  * Bootstrap NavProgressItem class
29844  * @cfg {String} rid the reference id
29845  * @cfg {Boolean} active (true|false) Is item active default false
29846  * @cfg {Boolean} disabled (true|false) Is item active default false
29847  * @cfg {String} html
29848  * @cfg {String} position (top|bottom) text position default bottom
29849  * @cfg {String} icon show icon instead of number
29850  * 
29851  * @constructor
29852  * Create a new NavProgressItem
29853  * @param {Object} config The config object
29854  */
29855 Roo.bootstrap.NavProgressItem = function(config){
29856     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29857     this.addEvents({
29858         // raw events
29859         /**
29860          * @event click
29861          * The raw click event for the entire grid.
29862          * @param {Roo.bootstrap.NavProgressItem} this
29863          * @param {Roo.EventObject} e
29864          */
29865         "click" : true
29866     });
29867    
29868 };
29869
29870 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29871     
29872     rid : '',
29873     active : false,
29874     disabled : false,
29875     html : '',
29876     position : 'bottom',
29877     icon : false,
29878     
29879     getAutoCreate : function()
29880     {
29881         var iconCls = 'roo-navigation-bar-item-icon';
29882         
29883         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29884         
29885         var cfg = {
29886             tag: 'li',
29887             cls: 'roo-navigation-bar-item',
29888             cn : [
29889                 {
29890                     tag : 'i',
29891                     cls : iconCls
29892                 }
29893             ]
29894         };
29895         
29896         if(this.active){
29897             cfg.cls += ' active';
29898         }
29899         if(this.disabled){
29900             cfg.cls += ' disabled';
29901         }
29902         
29903         return cfg;
29904     },
29905     
29906     disable : function()
29907     {
29908         this.setDisabled(true);
29909     },
29910     
29911     enable : function()
29912     {
29913         this.setDisabled(false);
29914     },
29915     
29916     initEvents: function() 
29917     {
29918         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29919         
29920         this.iconEl.on('click', this.onClick, this);
29921     },
29922     
29923     onClick : function(e)
29924     {
29925         e.preventDefault();
29926         
29927         if(this.disabled){
29928             return;
29929         }
29930         
29931         if(this.fireEvent('click', this, e) === false){
29932             return;
29933         };
29934         
29935         this.parent().setActiveItem(this);
29936     },
29937     
29938     isActive: function () 
29939     {
29940         return this.active;
29941     },
29942     
29943     setActive : function(state)
29944     {
29945         if(this.active == state){
29946             return;
29947         }
29948         
29949         this.active = state;
29950         
29951         if (state) {
29952             this.el.addClass('active');
29953             return;
29954         }
29955         
29956         this.el.removeClass('active');
29957         
29958         return;
29959     },
29960     
29961     setDisabled : function(state)
29962     {
29963         if(this.disabled == state){
29964             return;
29965         }
29966         
29967         this.disabled = state;
29968         
29969         if (state) {
29970             this.el.addClass('disabled');
29971             return;
29972         }
29973         
29974         this.el.removeClass('disabled');
29975     },
29976     
29977     tooltipEl : function()
29978     {
29979         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29980     }
29981 });
29982  
29983
29984  /*
29985  * - LGPL
29986  *
29987  * FieldLabel
29988  * 
29989  */
29990
29991 /**
29992  * @class Roo.bootstrap.FieldLabel
29993  * @extends Roo.bootstrap.Component
29994  * Bootstrap FieldLabel class
29995  * @cfg {String} html contents of the element
29996  * @cfg {String} tag tag of the element default label
29997  * @cfg {String} cls class of the element
29998  * @cfg {String} target label target 
29999  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30000  * @cfg {String} invalidClass default "text-warning"
30001  * @cfg {String} validClass default "text-success"
30002  * @cfg {String} iconTooltip default "This field is required"
30003  * @cfg {String} indicatorpos (left|right) default left
30004  * 
30005  * @constructor
30006  * Create a new FieldLabel
30007  * @param {Object} config The config object
30008  */
30009
30010 Roo.bootstrap.FieldLabel = function(config){
30011     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30012     
30013     this.addEvents({
30014             /**
30015              * @event invalid
30016              * Fires after the field has been marked as invalid.
30017              * @param {Roo.form.FieldLabel} this
30018              * @param {String} msg The validation message
30019              */
30020             invalid : true,
30021             /**
30022              * @event valid
30023              * Fires after the field has been validated with no errors.
30024              * @param {Roo.form.FieldLabel} this
30025              */
30026             valid : true
30027         });
30028 };
30029
30030 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30031     
30032     tag: 'label',
30033     cls: '',
30034     html: '',
30035     target: '',
30036     allowBlank : true,
30037     invalidClass : 'has-warning',
30038     validClass : 'has-success',
30039     iconTooltip : 'This field is required',
30040     indicatorpos : 'left',
30041     
30042     getAutoCreate : function(){
30043         
30044         var cls = "";
30045         if (!this.allowBlank) {
30046             cls  = "visible";
30047         }
30048         
30049         var cfg = {
30050             tag : this.tag,
30051             cls : 'roo-bootstrap-field-label ' + this.cls,
30052             for : this.target,
30053             cn : [
30054                 {
30055                     tag : 'i',
30056                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30057                     tooltip : this.iconTooltip
30058                 },
30059                 {
30060                     tag : 'span',
30061                     html : this.html
30062                 }
30063             ] 
30064         };
30065         
30066         if(this.indicatorpos == 'right'){
30067             var cfg = {
30068                 tag : this.tag,
30069                 cls : 'roo-bootstrap-field-label ' + this.cls,
30070                 for : this.target,
30071                 cn : [
30072                     {
30073                         tag : 'span',
30074                         html : this.html
30075                     },
30076                     {
30077                         tag : 'i',
30078                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30079                         tooltip : this.iconTooltip
30080                     }
30081                 ] 
30082             };
30083         }
30084         
30085         return cfg;
30086     },
30087     
30088     initEvents: function() 
30089     {
30090         Roo.bootstrap.Element.superclass.initEvents.call(this);
30091         
30092         this.indicator = this.indicatorEl();
30093         
30094         if(this.indicator){
30095             this.indicator.removeClass('visible');
30096             this.indicator.addClass('invisible');
30097         }
30098         
30099         Roo.bootstrap.FieldLabel.register(this);
30100     },
30101     
30102     indicatorEl : function()
30103     {
30104         var indicator = this.el.select('i.roo-required-indicator',true).first();
30105         
30106         if(!indicator){
30107             return false;
30108         }
30109         
30110         return indicator;
30111         
30112     },
30113     
30114     /**
30115      * Mark this field as valid
30116      */
30117     markValid : function()
30118     {
30119         if(this.indicator){
30120             this.indicator.removeClass('visible');
30121             this.indicator.addClass('invisible');
30122         }
30123         
30124         this.el.removeClass(this.invalidClass);
30125         
30126         this.el.addClass(this.validClass);
30127         
30128         this.fireEvent('valid', this);
30129     },
30130     
30131     /**
30132      * Mark this field as invalid
30133      * @param {String} msg The validation message
30134      */
30135     markInvalid : function(msg)
30136     {
30137         if(this.indicator){
30138             this.indicator.removeClass('invisible');
30139             this.indicator.addClass('visible');
30140         }
30141         
30142         this.el.removeClass(this.validClass);
30143         
30144         this.el.addClass(this.invalidClass);
30145         
30146         this.fireEvent('invalid', this, msg);
30147     }
30148     
30149    
30150 });
30151
30152 Roo.apply(Roo.bootstrap.FieldLabel, {
30153     
30154     groups: {},
30155     
30156      /**
30157     * register a FieldLabel Group
30158     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30159     */
30160     register : function(label)
30161     {
30162         if(this.groups.hasOwnProperty(label.target)){
30163             return;
30164         }
30165      
30166         this.groups[label.target] = label;
30167         
30168     },
30169     /**
30170     * fetch a FieldLabel Group based on the target
30171     * @param {string} target
30172     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30173     */
30174     get: function(target) {
30175         if (typeof(this.groups[target]) == 'undefined') {
30176             return false;
30177         }
30178         
30179         return this.groups[target] ;
30180     }
30181 });
30182
30183  
30184
30185  /*
30186  * - LGPL
30187  *
30188  * page DateSplitField.
30189  * 
30190  */
30191
30192
30193 /**
30194  * @class Roo.bootstrap.DateSplitField
30195  * @extends Roo.bootstrap.Component
30196  * Bootstrap DateSplitField class
30197  * @cfg {string} fieldLabel - the label associated
30198  * @cfg {Number} labelWidth set the width of label (0-12)
30199  * @cfg {String} labelAlign (top|left)
30200  * @cfg {Boolean} dayAllowBlank (true|false) default false
30201  * @cfg {Boolean} monthAllowBlank (true|false) default false
30202  * @cfg {Boolean} yearAllowBlank (true|false) default false
30203  * @cfg {string} dayPlaceholder 
30204  * @cfg {string} monthPlaceholder
30205  * @cfg {string} yearPlaceholder
30206  * @cfg {string} dayFormat default 'd'
30207  * @cfg {string} monthFormat default 'm'
30208  * @cfg {string} yearFormat default 'Y'
30209  * @cfg {Number} labellg set the width of label (1-12)
30210  * @cfg {Number} labelmd set the width of label (1-12)
30211  * @cfg {Number} labelsm set the width of label (1-12)
30212  * @cfg {Number} labelxs set the width of label (1-12)
30213
30214  *     
30215  * @constructor
30216  * Create a new DateSplitField
30217  * @param {Object} config The config object
30218  */
30219
30220 Roo.bootstrap.DateSplitField = function(config){
30221     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30222     
30223     this.addEvents({
30224         // raw events
30225          /**
30226          * @event years
30227          * getting the data of years
30228          * @param {Roo.bootstrap.DateSplitField} this
30229          * @param {Object} years
30230          */
30231         "years" : true,
30232         /**
30233          * @event days
30234          * getting the data of days
30235          * @param {Roo.bootstrap.DateSplitField} this
30236          * @param {Object} days
30237          */
30238         "days" : true,
30239         /**
30240          * @event invalid
30241          * Fires after the field has been marked as invalid.
30242          * @param {Roo.form.Field} this
30243          * @param {String} msg The validation message
30244          */
30245         invalid : true,
30246        /**
30247          * @event valid
30248          * Fires after the field has been validated with no errors.
30249          * @param {Roo.form.Field} this
30250          */
30251         valid : true
30252     });
30253 };
30254
30255 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30256     
30257     fieldLabel : '',
30258     labelAlign : 'top',
30259     labelWidth : 3,
30260     dayAllowBlank : false,
30261     monthAllowBlank : false,
30262     yearAllowBlank : false,
30263     dayPlaceholder : '',
30264     monthPlaceholder : '',
30265     yearPlaceholder : '',
30266     dayFormat : 'd',
30267     monthFormat : 'm',
30268     yearFormat : 'Y',
30269     isFormField : true,
30270     labellg : 0,
30271     labelmd : 0,
30272     labelsm : 0,
30273     labelxs : 0,
30274     
30275     getAutoCreate : function()
30276     {
30277         var cfg = {
30278             tag : 'div',
30279             cls : 'row roo-date-split-field-group',
30280             cn : [
30281                 {
30282                     tag : 'input',
30283                     type : 'hidden',
30284                     cls : 'form-hidden-field roo-date-split-field-group-value',
30285                     name : this.name
30286                 }
30287             ]
30288         };
30289         
30290         var labelCls = 'col-md-12';
30291         var contentCls = 'col-md-4';
30292         
30293         if(this.fieldLabel){
30294             
30295             var label = {
30296                 tag : 'div',
30297                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30298                 cn : [
30299                     {
30300                         tag : 'label',
30301                         html : this.fieldLabel
30302                     }
30303                 ]
30304             };
30305             
30306             if(this.labelAlign == 'left'){
30307             
30308                 if(this.labelWidth > 12){
30309                     label.style = "width: " + this.labelWidth + 'px';
30310                 }
30311
30312                 if(this.labelWidth < 13 && this.labelmd == 0){
30313                     this.labelmd = this.labelWidth;
30314                 }
30315
30316                 if(this.labellg > 0){
30317                     labelCls = ' col-lg-' + this.labellg;
30318                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30319                 }
30320
30321                 if(this.labelmd > 0){
30322                     labelCls = ' col-md-' + this.labelmd;
30323                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30324                 }
30325
30326                 if(this.labelsm > 0){
30327                     labelCls = ' col-sm-' + this.labelsm;
30328                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30329                 }
30330
30331                 if(this.labelxs > 0){
30332                     labelCls = ' col-xs-' + this.labelxs;
30333                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30334                 }
30335             }
30336             
30337             label.cls += ' ' + labelCls;
30338             
30339             cfg.cn.push(label);
30340         }
30341         
30342         Roo.each(['day', 'month', 'year'], function(t){
30343             cfg.cn.push({
30344                 tag : 'div',
30345                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30346             });
30347         }, this);
30348         
30349         return cfg;
30350     },
30351     
30352     inputEl: function ()
30353     {
30354         return this.el.select('.roo-date-split-field-group-value', true).first();
30355     },
30356     
30357     onRender : function(ct, position) 
30358     {
30359         var _this = this;
30360         
30361         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30362         
30363         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30364         
30365         this.dayField = new Roo.bootstrap.ComboBox({
30366             allowBlank : this.dayAllowBlank,
30367             alwaysQuery : true,
30368             displayField : 'value',
30369             editable : false,
30370             fieldLabel : '',
30371             forceSelection : true,
30372             mode : 'local',
30373             placeholder : this.dayPlaceholder,
30374             selectOnFocus : true,
30375             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30376             triggerAction : 'all',
30377             typeAhead : true,
30378             valueField : 'value',
30379             store : new Roo.data.SimpleStore({
30380                 data : (function() {    
30381                     var days = [];
30382                     _this.fireEvent('days', _this, days);
30383                     return days;
30384                 })(),
30385                 fields : [ 'value' ]
30386             }),
30387             listeners : {
30388                 select : function (_self, record, index)
30389                 {
30390                     _this.setValue(_this.getValue());
30391                 }
30392             }
30393         });
30394
30395         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30396         
30397         this.monthField = new Roo.bootstrap.MonthField({
30398             after : '<i class=\"fa fa-calendar\"></i>',
30399             allowBlank : this.monthAllowBlank,
30400             placeholder : this.monthPlaceholder,
30401             readOnly : true,
30402             listeners : {
30403                 render : function (_self)
30404                 {
30405                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30406                         e.preventDefault();
30407                         _self.focus();
30408                     });
30409                 },
30410                 select : function (_self, oldvalue, newvalue)
30411                 {
30412                     _this.setValue(_this.getValue());
30413                 }
30414             }
30415         });
30416         
30417         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30418         
30419         this.yearField = new Roo.bootstrap.ComboBox({
30420             allowBlank : this.yearAllowBlank,
30421             alwaysQuery : true,
30422             displayField : 'value',
30423             editable : false,
30424             fieldLabel : '',
30425             forceSelection : true,
30426             mode : 'local',
30427             placeholder : this.yearPlaceholder,
30428             selectOnFocus : true,
30429             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30430             triggerAction : 'all',
30431             typeAhead : true,
30432             valueField : 'value',
30433             store : new Roo.data.SimpleStore({
30434                 data : (function() {
30435                     var years = [];
30436                     _this.fireEvent('years', _this, years);
30437                     return years;
30438                 })(),
30439                 fields : [ 'value' ]
30440             }),
30441             listeners : {
30442                 select : function (_self, record, index)
30443                 {
30444                     _this.setValue(_this.getValue());
30445                 }
30446             }
30447         });
30448
30449         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30450     },
30451     
30452     setValue : function(v, format)
30453     {
30454         this.inputEl.dom.value = v;
30455         
30456         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30457         
30458         var d = Date.parseDate(v, f);
30459         
30460         if(!d){
30461             this.validate();
30462             return;
30463         }
30464         
30465         this.setDay(d.format(this.dayFormat));
30466         this.setMonth(d.format(this.monthFormat));
30467         this.setYear(d.format(this.yearFormat));
30468         
30469         this.validate();
30470         
30471         return;
30472     },
30473     
30474     setDay : function(v)
30475     {
30476         this.dayField.setValue(v);
30477         this.inputEl.dom.value = this.getValue();
30478         this.validate();
30479         return;
30480     },
30481     
30482     setMonth : function(v)
30483     {
30484         this.monthField.setValue(v, true);
30485         this.inputEl.dom.value = this.getValue();
30486         this.validate();
30487         return;
30488     },
30489     
30490     setYear : function(v)
30491     {
30492         this.yearField.setValue(v);
30493         this.inputEl.dom.value = this.getValue();
30494         this.validate();
30495         return;
30496     },
30497     
30498     getDay : function()
30499     {
30500         return this.dayField.getValue();
30501     },
30502     
30503     getMonth : function()
30504     {
30505         return this.monthField.getValue();
30506     },
30507     
30508     getYear : function()
30509     {
30510         return this.yearField.getValue();
30511     },
30512     
30513     getValue : function()
30514     {
30515         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30516         
30517         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30518         
30519         return date;
30520     },
30521     
30522     reset : function()
30523     {
30524         this.setDay('');
30525         this.setMonth('');
30526         this.setYear('');
30527         this.inputEl.dom.value = '';
30528         this.validate();
30529         return;
30530     },
30531     
30532     validate : function()
30533     {
30534         var d = this.dayField.validate();
30535         var m = this.monthField.validate();
30536         var y = this.yearField.validate();
30537         
30538         var valid = true;
30539         
30540         if(
30541                 (!this.dayAllowBlank && !d) ||
30542                 (!this.monthAllowBlank && !m) ||
30543                 (!this.yearAllowBlank && !y)
30544         ){
30545             valid = false;
30546         }
30547         
30548         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30549             return valid;
30550         }
30551         
30552         if(valid){
30553             this.markValid();
30554             return valid;
30555         }
30556         
30557         this.markInvalid();
30558         
30559         return valid;
30560     },
30561     
30562     markValid : function()
30563     {
30564         
30565         var label = this.el.select('label', true).first();
30566         var icon = this.el.select('i.fa-star', true).first();
30567
30568         if(label && icon){
30569             icon.remove();
30570         }
30571         
30572         this.fireEvent('valid', this);
30573     },
30574     
30575      /**
30576      * Mark this field as invalid
30577      * @param {String} msg The validation message
30578      */
30579     markInvalid : function(msg)
30580     {
30581         
30582         var label = this.el.select('label', true).first();
30583         var icon = this.el.select('i.fa-star', true).first();
30584
30585         if(label && !icon){
30586             this.el.select('.roo-date-split-field-label', true).createChild({
30587                 tag : 'i',
30588                 cls : 'text-danger fa fa-lg fa-star',
30589                 tooltip : 'This field is required',
30590                 style : 'margin-right:5px;'
30591             }, label, true);
30592         }
30593         
30594         this.fireEvent('invalid', this, msg);
30595     },
30596     
30597     clearInvalid : function()
30598     {
30599         var label = this.el.select('label', true).first();
30600         var icon = this.el.select('i.fa-star', true).first();
30601
30602         if(label && icon){
30603             icon.remove();
30604         }
30605         
30606         this.fireEvent('valid', this);
30607     },
30608     
30609     getName: function()
30610     {
30611         return this.name;
30612     }
30613     
30614 });
30615
30616  /**
30617  *
30618  * This is based on 
30619  * http://masonry.desandro.com
30620  *
30621  * The idea is to render all the bricks based on vertical width...
30622  *
30623  * The original code extends 'outlayer' - we might need to use that....
30624  * 
30625  */
30626
30627
30628 /**
30629  * @class Roo.bootstrap.LayoutMasonry
30630  * @extends Roo.bootstrap.Component
30631  * Bootstrap Layout Masonry class
30632  * 
30633  * @constructor
30634  * Create a new Element
30635  * @param {Object} config The config object
30636  */
30637
30638 Roo.bootstrap.LayoutMasonry = function(config){
30639     
30640     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30641     
30642     this.bricks = [];
30643     
30644     Roo.bootstrap.LayoutMasonry.register(this);
30645     
30646     this.addEvents({
30647         // raw events
30648         /**
30649          * @event layout
30650          * Fire after layout the items
30651          * @param {Roo.bootstrap.LayoutMasonry} this
30652          * @param {Roo.EventObject} e
30653          */
30654         "layout" : true
30655     });
30656     
30657 };
30658
30659 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30660     
30661     /**
30662      * @cfg {Boolean} isLayoutInstant = no animation?
30663      */   
30664     isLayoutInstant : false, // needed?
30665    
30666     /**
30667      * @cfg {Number} boxWidth  width of the columns
30668      */   
30669     boxWidth : 450,
30670     
30671       /**
30672      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30673      */   
30674     boxHeight : 0,
30675     
30676     /**
30677      * @cfg {Number} padWidth padding below box..
30678      */   
30679     padWidth : 10, 
30680     
30681     /**
30682      * @cfg {Number} gutter gutter width..
30683      */   
30684     gutter : 10,
30685     
30686      /**
30687      * @cfg {Number} maxCols maximum number of columns
30688      */   
30689     
30690     maxCols: 0,
30691     
30692     /**
30693      * @cfg {Boolean} isAutoInitial defalut true
30694      */   
30695     isAutoInitial : true, 
30696     
30697     containerWidth: 0,
30698     
30699     /**
30700      * @cfg {Boolean} isHorizontal defalut false
30701      */   
30702     isHorizontal : false, 
30703
30704     currentSize : null,
30705     
30706     tag: 'div',
30707     
30708     cls: '',
30709     
30710     bricks: null, //CompositeElement
30711     
30712     cols : 1,
30713     
30714     _isLayoutInited : false,
30715     
30716 //    isAlternative : false, // only use for vertical layout...
30717     
30718     /**
30719      * @cfg {Number} alternativePadWidth padding below box..
30720      */   
30721     alternativePadWidth : 50,
30722     
30723     selectedBrick : [],
30724     
30725     getAutoCreate : function(){
30726         
30727         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30728         
30729         var cfg = {
30730             tag: this.tag,
30731             cls: 'blog-masonary-wrapper ' + this.cls,
30732             cn : {
30733                 cls : 'mas-boxes masonary'
30734             }
30735         };
30736         
30737         return cfg;
30738     },
30739     
30740     getChildContainer: function( )
30741     {
30742         if (this.boxesEl) {
30743             return this.boxesEl;
30744         }
30745         
30746         this.boxesEl = this.el.select('.mas-boxes').first();
30747         
30748         return this.boxesEl;
30749     },
30750     
30751     
30752     initEvents : function()
30753     {
30754         var _this = this;
30755         
30756         if(this.isAutoInitial){
30757             Roo.log('hook children rendered');
30758             this.on('childrenrendered', function() {
30759                 Roo.log('children rendered');
30760                 _this.initial();
30761             } ,this);
30762         }
30763     },
30764     
30765     initial : function()
30766     {
30767         this.selectedBrick = [];
30768         
30769         this.currentSize = this.el.getBox(true);
30770         
30771         Roo.EventManager.onWindowResize(this.resize, this); 
30772
30773         if(!this.isAutoInitial){
30774             this.layout();
30775             return;
30776         }
30777         
30778         this.layout();
30779         
30780         return;
30781         //this.layout.defer(500,this);
30782         
30783     },
30784     
30785     resize : function()
30786     {
30787         var cs = this.el.getBox(true);
30788         
30789         if (
30790                 this.currentSize.width == cs.width && 
30791                 this.currentSize.x == cs.x && 
30792                 this.currentSize.height == cs.height && 
30793                 this.currentSize.y == cs.y 
30794         ) {
30795             Roo.log("no change in with or X or Y");
30796             return;
30797         }
30798         
30799         this.currentSize = cs;
30800         
30801         this.layout();
30802         
30803     },
30804     
30805     layout : function()
30806     {   
30807         this._resetLayout();
30808         
30809         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30810         
30811         this.layoutItems( isInstant );
30812       
30813         this._isLayoutInited = true;
30814         
30815         this.fireEvent('layout', this);
30816         
30817     },
30818     
30819     _resetLayout : function()
30820     {
30821         if(this.isHorizontal){
30822             this.horizontalMeasureColumns();
30823             return;
30824         }
30825         
30826         this.verticalMeasureColumns();
30827         
30828     },
30829     
30830     verticalMeasureColumns : function()
30831     {
30832         this.getContainerWidth();
30833         
30834 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30835 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30836 //            return;
30837 //        }
30838         
30839         var boxWidth = this.boxWidth + this.padWidth;
30840         
30841         if(this.containerWidth < this.boxWidth){
30842             boxWidth = this.containerWidth
30843         }
30844         
30845         var containerWidth = this.containerWidth;
30846         
30847         var cols = Math.floor(containerWidth / boxWidth);
30848         
30849         this.cols = Math.max( cols, 1 );
30850         
30851         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30852         
30853         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30854         
30855         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30856         
30857         this.colWidth = boxWidth + avail - this.padWidth;
30858         
30859         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30860         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30861     },
30862     
30863     horizontalMeasureColumns : function()
30864     {
30865         this.getContainerWidth();
30866         
30867         var boxWidth = this.boxWidth;
30868         
30869         if(this.containerWidth < boxWidth){
30870             boxWidth = this.containerWidth;
30871         }
30872         
30873         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30874         
30875         this.el.setHeight(boxWidth);
30876         
30877     },
30878     
30879     getContainerWidth : function()
30880     {
30881         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30882     },
30883     
30884     layoutItems : function( isInstant )
30885     {
30886         Roo.log(this.bricks);
30887         
30888         var items = Roo.apply([], this.bricks);
30889         
30890         if(this.isHorizontal){
30891             this._horizontalLayoutItems( items , isInstant );
30892             return;
30893         }
30894         
30895 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30896 //            this._verticalAlternativeLayoutItems( items , isInstant );
30897 //            return;
30898 //        }
30899         
30900         this._verticalLayoutItems( items , isInstant );
30901         
30902     },
30903     
30904     _verticalLayoutItems : function ( items , isInstant)
30905     {
30906         if ( !items || !items.length ) {
30907             return;
30908         }
30909         
30910         var standard = [
30911             ['xs', 'xs', 'xs', 'tall'],
30912             ['xs', 'xs', 'tall'],
30913             ['xs', 'xs', 'sm'],
30914             ['xs', 'xs', 'xs'],
30915             ['xs', 'tall'],
30916             ['xs', 'sm'],
30917             ['xs', 'xs'],
30918             ['xs'],
30919             
30920             ['sm', 'xs', 'xs'],
30921             ['sm', 'xs'],
30922             ['sm'],
30923             
30924             ['tall', 'xs', 'xs', 'xs'],
30925             ['tall', 'xs', 'xs'],
30926             ['tall', 'xs'],
30927             ['tall']
30928             
30929         ];
30930         
30931         var queue = [];
30932         
30933         var boxes = [];
30934         
30935         var box = [];
30936         
30937         Roo.each(items, function(item, k){
30938             
30939             switch (item.size) {
30940                 // these layouts take up a full box,
30941                 case 'md' :
30942                 case 'md-left' :
30943                 case 'md-right' :
30944                 case 'wide' :
30945                     
30946                     if(box.length){
30947                         boxes.push(box);
30948                         box = [];
30949                     }
30950                     
30951                     boxes.push([item]);
30952                     
30953                     break;
30954                     
30955                 case 'xs' :
30956                 case 'sm' :
30957                 case 'tall' :
30958                     
30959                     box.push(item);
30960                     
30961                     break;
30962                 default :
30963                     break;
30964                     
30965             }
30966             
30967         }, this);
30968         
30969         if(box.length){
30970             boxes.push(box);
30971             box = [];
30972         }
30973         
30974         var filterPattern = function(box, length)
30975         {
30976             if(!box.length){
30977                 return;
30978             }
30979             
30980             var match = false;
30981             
30982             var pattern = box.slice(0, length);
30983             
30984             var format = [];
30985             
30986             Roo.each(pattern, function(i){
30987                 format.push(i.size);
30988             }, this);
30989             
30990             Roo.each(standard, function(s){
30991                 
30992                 if(String(s) != String(format)){
30993                     return;
30994                 }
30995                 
30996                 match = true;
30997                 return false;
30998                 
30999             }, this);
31000             
31001             if(!match && length == 1){
31002                 return;
31003             }
31004             
31005             if(!match){
31006                 filterPattern(box, length - 1);
31007                 return;
31008             }
31009                 
31010             queue.push(pattern);
31011
31012             box = box.slice(length, box.length);
31013
31014             filterPattern(box, 4);
31015
31016             return;
31017             
31018         }
31019         
31020         Roo.each(boxes, function(box, k){
31021             
31022             if(!box.length){
31023                 return;
31024             }
31025             
31026             if(box.length == 1){
31027                 queue.push(box);
31028                 return;
31029             }
31030             
31031             filterPattern(box, 4);
31032             
31033         }, this);
31034         
31035         this._processVerticalLayoutQueue( queue, isInstant );
31036         
31037     },
31038     
31039 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31040 //    {
31041 //        if ( !items || !items.length ) {
31042 //            return;
31043 //        }
31044 //
31045 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31046 //        
31047 //    },
31048     
31049     _horizontalLayoutItems : function ( items , isInstant)
31050     {
31051         if ( !items || !items.length || items.length < 3) {
31052             return;
31053         }
31054         
31055         items.reverse();
31056         
31057         var eItems = items.slice(0, 3);
31058         
31059         items = items.slice(3, items.length);
31060         
31061         var standard = [
31062             ['xs', 'xs', 'xs', 'wide'],
31063             ['xs', 'xs', 'wide'],
31064             ['xs', 'xs', 'sm'],
31065             ['xs', 'xs', 'xs'],
31066             ['xs', 'wide'],
31067             ['xs', 'sm'],
31068             ['xs', 'xs'],
31069             ['xs'],
31070             
31071             ['sm', 'xs', 'xs'],
31072             ['sm', 'xs'],
31073             ['sm'],
31074             
31075             ['wide', 'xs', 'xs', 'xs'],
31076             ['wide', 'xs', 'xs'],
31077             ['wide', 'xs'],
31078             ['wide'],
31079             
31080             ['wide-thin']
31081         ];
31082         
31083         var queue = [];
31084         
31085         var boxes = [];
31086         
31087         var box = [];
31088         
31089         Roo.each(items, function(item, k){
31090             
31091             switch (item.size) {
31092                 case 'md' :
31093                 case 'md-left' :
31094                 case 'md-right' :
31095                 case 'tall' :
31096                     
31097                     if(box.length){
31098                         boxes.push(box);
31099                         box = [];
31100                     }
31101                     
31102                     boxes.push([item]);
31103                     
31104                     break;
31105                     
31106                 case 'xs' :
31107                 case 'sm' :
31108                 case 'wide' :
31109                 case 'wide-thin' :
31110                     
31111                     box.push(item);
31112                     
31113                     break;
31114                 default :
31115                     break;
31116                     
31117             }
31118             
31119         }, this);
31120         
31121         if(box.length){
31122             boxes.push(box);
31123             box = [];
31124         }
31125         
31126         var filterPattern = function(box, length)
31127         {
31128             if(!box.length){
31129                 return;
31130             }
31131             
31132             var match = false;
31133             
31134             var pattern = box.slice(0, length);
31135             
31136             var format = [];
31137             
31138             Roo.each(pattern, function(i){
31139                 format.push(i.size);
31140             }, this);
31141             
31142             Roo.each(standard, function(s){
31143                 
31144                 if(String(s) != String(format)){
31145                     return;
31146                 }
31147                 
31148                 match = true;
31149                 return false;
31150                 
31151             }, this);
31152             
31153             if(!match && length == 1){
31154                 return;
31155             }
31156             
31157             if(!match){
31158                 filterPattern(box, length - 1);
31159                 return;
31160             }
31161                 
31162             queue.push(pattern);
31163
31164             box = box.slice(length, box.length);
31165
31166             filterPattern(box, 4);
31167
31168             return;
31169             
31170         }
31171         
31172         Roo.each(boxes, function(box, k){
31173             
31174             if(!box.length){
31175                 return;
31176             }
31177             
31178             if(box.length == 1){
31179                 queue.push(box);
31180                 return;
31181             }
31182             
31183             filterPattern(box, 4);
31184             
31185         }, this);
31186         
31187         
31188         var prune = [];
31189         
31190         var pos = this.el.getBox(true);
31191         
31192         var minX = pos.x;
31193         
31194         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31195         
31196         var hit_end = false;
31197         
31198         Roo.each(queue, function(box){
31199             
31200             if(hit_end){
31201                 
31202                 Roo.each(box, function(b){
31203                 
31204                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31205                     b.el.hide();
31206
31207                 }, this);
31208
31209                 return;
31210             }
31211             
31212             var mx = 0;
31213             
31214             Roo.each(box, function(b){
31215                 
31216                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31217                 b.el.show();
31218
31219                 mx = Math.max(mx, b.x);
31220                 
31221             }, this);
31222             
31223             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31224             
31225             if(maxX < minX){
31226                 
31227                 Roo.each(box, function(b){
31228                 
31229                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31230                     b.el.hide();
31231                     
31232                 }, this);
31233                 
31234                 hit_end = true;
31235                 
31236                 return;
31237             }
31238             
31239             prune.push(box);
31240             
31241         }, this);
31242         
31243         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31244     },
31245     
31246     /** Sets position of item in DOM
31247     * @param {Element} item
31248     * @param {Number} x - horizontal position
31249     * @param {Number} y - vertical position
31250     * @param {Boolean} isInstant - disables transitions
31251     */
31252     _processVerticalLayoutQueue : function( queue, isInstant )
31253     {
31254         var pos = this.el.getBox(true);
31255         var x = pos.x;
31256         var y = pos.y;
31257         var maxY = [];
31258         
31259         for (var i = 0; i < this.cols; i++){
31260             maxY[i] = pos.y;
31261         }
31262         
31263         Roo.each(queue, function(box, k){
31264             
31265             var col = k % this.cols;
31266             
31267             Roo.each(box, function(b,kk){
31268                 
31269                 b.el.position('absolute');
31270                 
31271                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31272                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31273                 
31274                 if(b.size == 'md-left' || b.size == 'md-right'){
31275                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31276                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31277                 }
31278                 
31279                 b.el.setWidth(width);
31280                 b.el.setHeight(height);
31281                 // iframe?
31282                 b.el.select('iframe',true).setSize(width,height);
31283                 
31284             }, this);
31285             
31286             for (var i = 0; i < this.cols; i++){
31287                 
31288                 if(maxY[i] < maxY[col]){
31289                     col = i;
31290                     continue;
31291                 }
31292                 
31293                 col = Math.min(col, i);
31294                 
31295             }
31296             
31297             x = pos.x + col * (this.colWidth + this.padWidth);
31298             
31299             y = maxY[col];
31300             
31301             var positions = [];
31302             
31303             switch (box.length){
31304                 case 1 :
31305                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31306                     break;
31307                 case 2 :
31308                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31309                     break;
31310                 case 3 :
31311                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31312                     break;
31313                 case 4 :
31314                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31315                     break;
31316                 default :
31317                     break;
31318             }
31319             
31320             Roo.each(box, function(b,kk){
31321                 
31322                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31323                 
31324                 var sz = b.el.getSize();
31325                 
31326                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31327                 
31328             }, this);
31329             
31330         }, this);
31331         
31332         var mY = 0;
31333         
31334         for (var i = 0; i < this.cols; i++){
31335             mY = Math.max(mY, maxY[i]);
31336         }
31337         
31338         this.el.setHeight(mY - pos.y);
31339         
31340     },
31341     
31342 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31343 //    {
31344 //        var pos = this.el.getBox(true);
31345 //        var x = pos.x;
31346 //        var y = pos.y;
31347 //        var maxX = pos.right;
31348 //        
31349 //        var maxHeight = 0;
31350 //        
31351 //        Roo.each(items, function(item, k){
31352 //            
31353 //            var c = k % 2;
31354 //            
31355 //            item.el.position('absolute');
31356 //                
31357 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31358 //
31359 //            item.el.setWidth(width);
31360 //
31361 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31362 //
31363 //            item.el.setHeight(height);
31364 //            
31365 //            if(c == 0){
31366 //                item.el.setXY([x, y], isInstant ? false : true);
31367 //            } else {
31368 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31369 //            }
31370 //            
31371 //            y = y + height + this.alternativePadWidth;
31372 //            
31373 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31374 //            
31375 //        }, this);
31376 //        
31377 //        this.el.setHeight(maxHeight);
31378 //        
31379 //    },
31380     
31381     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31382     {
31383         var pos = this.el.getBox(true);
31384         
31385         var minX = pos.x;
31386         var minY = pos.y;
31387         
31388         var maxX = pos.right;
31389         
31390         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31391         
31392         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31393         
31394         Roo.each(queue, function(box, k){
31395             
31396             Roo.each(box, function(b, kk){
31397                 
31398                 b.el.position('absolute');
31399                 
31400                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31401                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31402                 
31403                 if(b.size == 'md-left' || b.size == 'md-right'){
31404                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31405                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31406                 }
31407                 
31408                 b.el.setWidth(width);
31409                 b.el.setHeight(height);
31410                 
31411             }, this);
31412             
31413             if(!box.length){
31414                 return;
31415             }
31416             
31417             var positions = [];
31418             
31419             switch (box.length){
31420                 case 1 :
31421                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31422                     break;
31423                 case 2 :
31424                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31425                     break;
31426                 case 3 :
31427                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31428                     break;
31429                 case 4 :
31430                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31431                     break;
31432                 default :
31433                     break;
31434             }
31435             
31436             Roo.each(box, function(b,kk){
31437                 
31438                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31439                 
31440                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31441                 
31442             }, this);
31443             
31444         }, this);
31445         
31446     },
31447     
31448     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31449     {
31450         Roo.each(eItems, function(b,k){
31451             
31452             b.size = (k == 0) ? 'sm' : 'xs';
31453             b.x = (k == 0) ? 2 : 1;
31454             b.y = (k == 0) ? 2 : 1;
31455             
31456             b.el.position('absolute');
31457             
31458             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31459                 
31460             b.el.setWidth(width);
31461             
31462             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31463             
31464             b.el.setHeight(height);
31465             
31466         }, this);
31467
31468         var positions = [];
31469         
31470         positions.push({
31471             x : maxX - this.unitWidth * 2 - this.gutter,
31472             y : minY
31473         });
31474         
31475         positions.push({
31476             x : maxX - this.unitWidth,
31477             y : minY + (this.unitWidth + this.gutter) * 2
31478         });
31479         
31480         positions.push({
31481             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31482             y : minY
31483         });
31484         
31485         Roo.each(eItems, function(b,k){
31486             
31487             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31488
31489         }, this);
31490         
31491     },
31492     
31493     getVerticalOneBoxColPositions : function(x, y, box)
31494     {
31495         var pos = [];
31496         
31497         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31498         
31499         if(box[0].size == 'md-left'){
31500             rand = 0;
31501         }
31502         
31503         if(box[0].size == 'md-right'){
31504             rand = 1;
31505         }
31506         
31507         pos.push({
31508             x : x + (this.unitWidth + this.gutter) * rand,
31509             y : y
31510         });
31511         
31512         return pos;
31513     },
31514     
31515     getVerticalTwoBoxColPositions : function(x, y, box)
31516     {
31517         var pos = [];
31518         
31519         if(box[0].size == 'xs'){
31520             
31521             pos.push({
31522                 x : x,
31523                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31524             });
31525
31526             pos.push({
31527                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31528                 y : y
31529             });
31530             
31531             return pos;
31532             
31533         }
31534         
31535         pos.push({
31536             x : x,
31537             y : y
31538         });
31539
31540         pos.push({
31541             x : x + (this.unitWidth + this.gutter) * 2,
31542             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31543         });
31544         
31545         return pos;
31546         
31547     },
31548     
31549     getVerticalThreeBoxColPositions : function(x, y, box)
31550     {
31551         var pos = [];
31552         
31553         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31554             
31555             pos.push({
31556                 x : x,
31557                 y : y
31558             });
31559
31560             pos.push({
31561                 x : x + (this.unitWidth + this.gutter) * 1,
31562                 y : y
31563             });
31564             
31565             pos.push({
31566                 x : x + (this.unitWidth + this.gutter) * 2,
31567                 y : y
31568             });
31569             
31570             return pos;
31571             
31572         }
31573         
31574         if(box[0].size == 'xs' && box[1].size == 'xs'){
31575             
31576             pos.push({
31577                 x : x,
31578                 y : y
31579             });
31580
31581             pos.push({
31582                 x : x,
31583                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31584             });
31585             
31586             pos.push({
31587                 x : x + (this.unitWidth + this.gutter) * 1,
31588                 y : y
31589             });
31590             
31591             return pos;
31592             
31593         }
31594         
31595         pos.push({
31596             x : x,
31597             y : y
31598         });
31599
31600         pos.push({
31601             x : x + (this.unitWidth + this.gutter) * 2,
31602             y : y
31603         });
31604
31605         pos.push({
31606             x : x + (this.unitWidth + this.gutter) * 2,
31607             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31608         });
31609             
31610         return pos;
31611         
31612     },
31613     
31614     getVerticalFourBoxColPositions : function(x, y, box)
31615     {
31616         var pos = [];
31617         
31618         if(box[0].size == 'xs'){
31619             
31620             pos.push({
31621                 x : x,
31622                 y : y
31623             });
31624
31625             pos.push({
31626                 x : x,
31627                 y : y + (this.unitHeight + this.gutter) * 1
31628             });
31629             
31630             pos.push({
31631                 x : x,
31632                 y : y + (this.unitHeight + this.gutter) * 2
31633             });
31634             
31635             pos.push({
31636                 x : x + (this.unitWidth + this.gutter) * 1,
31637                 y : y
31638             });
31639             
31640             return pos;
31641             
31642         }
31643         
31644         pos.push({
31645             x : x,
31646             y : y
31647         });
31648
31649         pos.push({
31650             x : x + (this.unitWidth + this.gutter) * 2,
31651             y : y
31652         });
31653
31654         pos.push({
31655             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31656             y : y + (this.unitHeight + this.gutter) * 1
31657         });
31658
31659         pos.push({
31660             x : x + (this.unitWidth + this.gutter) * 2,
31661             y : y + (this.unitWidth + this.gutter) * 2
31662         });
31663
31664         return pos;
31665         
31666     },
31667     
31668     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31669     {
31670         var pos = [];
31671         
31672         if(box[0].size == 'md-left'){
31673             pos.push({
31674                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31675                 y : minY
31676             });
31677             
31678             return pos;
31679         }
31680         
31681         if(box[0].size == 'md-right'){
31682             pos.push({
31683                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31684                 y : minY + (this.unitWidth + this.gutter) * 1
31685             });
31686             
31687             return pos;
31688         }
31689         
31690         var rand = Math.floor(Math.random() * (4 - box[0].y));
31691         
31692         pos.push({
31693             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31694             y : minY + (this.unitWidth + this.gutter) * rand
31695         });
31696         
31697         return pos;
31698         
31699     },
31700     
31701     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31702     {
31703         var pos = [];
31704         
31705         if(box[0].size == 'xs'){
31706             
31707             pos.push({
31708                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31709                 y : minY
31710             });
31711
31712             pos.push({
31713                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31714                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31715             });
31716             
31717             return pos;
31718             
31719         }
31720         
31721         pos.push({
31722             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31723             y : minY
31724         });
31725
31726         pos.push({
31727             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31728             y : minY + (this.unitWidth + this.gutter) * 2
31729         });
31730         
31731         return pos;
31732         
31733     },
31734     
31735     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31736     {
31737         var pos = [];
31738         
31739         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31740             
31741             pos.push({
31742                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31743                 y : minY
31744             });
31745
31746             pos.push({
31747                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31748                 y : minY + (this.unitWidth + this.gutter) * 1
31749             });
31750             
31751             pos.push({
31752                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31753                 y : minY + (this.unitWidth + this.gutter) * 2
31754             });
31755             
31756             return pos;
31757             
31758         }
31759         
31760         if(box[0].size == 'xs' && box[1].size == 'xs'){
31761             
31762             pos.push({
31763                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31764                 y : minY
31765             });
31766
31767             pos.push({
31768                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31769                 y : minY
31770             });
31771             
31772             pos.push({
31773                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31774                 y : minY + (this.unitWidth + this.gutter) * 1
31775             });
31776             
31777             return pos;
31778             
31779         }
31780         
31781         pos.push({
31782             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31783             y : minY
31784         });
31785
31786         pos.push({
31787             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31788             y : minY + (this.unitWidth + this.gutter) * 2
31789         });
31790
31791         pos.push({
31792             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31793             y : minY + (this.unitWidth + this.gutter) * 2
31794         });
31795             
31796         return pos;
31797         
31798     },
31799     
31800     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31801     {
31802         var pos = [];
31803         
31804         if(box[0].size == 'xs'){
31805             
31806             pos.push({
31807                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31808                 y : minY
31809             });
31810
31811             pos.push({
31812                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31813                 y : minY
31814             });
31815             
31816             pos.push({
31817                 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),
31818                 y : minY
31819             });
31820             
31821             pos.push({
31822                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31823                 y : minY + (this.unitWidth + this.gutter) * 1
31824             });
31825             
31826             return pos;
31827             
31828         }
31829         
31830         pos.push({
31831             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31832             y : minY
31833         });
31834         
31835         pos.push({
31836             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31837             y : minY + (this.unitWidth + this.gutter) * 2
31838         });
31839         
31840         pos.push({
31841             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31842             y : minY + (this.unitWidth + this.gutter) * 2
31843         });
31844         
31845         pos.push({
31846             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),
31847             y : minY + (this.unitWidth + this.gutter) * 2
31848         });
31849
31850         return pos;
31851         
31852     },
31853     
31854     /**
31855     * remove a Masonry Brick
31856     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31857     */
31858     removeBrick : function(brick_id)
31859     {
31860         if (!brick_id) {
31861             return;
31862         }
31863         
31864         for (var i = 0; i<this.bricks.length; i++) {
31865             if (this.bricks[i].id == brick_id) {
31866                 this.bricks.splice(i,1);
31867                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31868                 this.initial();
31869             }
31870         }
31871     },
31872     
31873     /**
31874     * adds a Masonry Brick
31875     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31876     */
31877     addBrick : function(cfg)
31878     {
31879         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31880         //this.register(cn);
31881         cn.parentId = this.id;
31882         cn.onRender(this.el, null);
31883         return cn;
31884     },
31885     
31886     /**
31887     * register a Masonry Brick
31888     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31889     */
31890     
31891     register : function(brick)
31892     {
31893         this.bricks.push(brick);
31894         brick.masonryId = this.id;
31895     },
31896     
31897     /**
31898     * clear all the Masonry Brick
31899     */
31900     clearAll : function()
31901     {
31902         this.bricks = [];
31903         //this.getChildContainer().dom.innerHTML = "";
31904         this.el.dom.innerHTML = '';
31905     },
31906     
31907     getSelected : function()
31908     {
31909         if (!this.selectedBrick) {
31910             return false;
31911         }
31912         
31913         return this.selectedBrick;
31914     }
31915 });
31916
31917 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31918     
31919     groups: {},
31920      /**
31921     * register a Masonry Layout
31922     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31923     */
31924     
31925     register : function(layout)
31926     {
31927         this.groups[layout.id] = layout;
31928     },
31929     /**
31930     * fetch a  Masonry Layout based on the masonry layout ID
31931     * @param {string} the masonry layout to add
31932     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31933     */
31934     
31935     get: function(layout_id) {
31936         if (typeof(this.groups[layout_id]) == 'undefined') {
31937             return false;
31938         }
31939         return this.groups[layout_id] ;
31940     }
31941     
31942     
31943     
31944 });
31945
31946  
31947
31948  /**
31949  *
31950  * This is based on 
31951  * http://masonry.desandro.com
31952  *
31953  * The idea is to render all the bricks based on vertical width...
31954  *
31955  * The original code extends 'outlayer' - we might need to use that....
31956  * 
31957  */
31958
31959
31960 /**
31961  * @class Roo.bootstrap.LayoutMasonryAuto
31962  * @extends Roo.bootstrap.Component
31963  * Bootstrap Layout Masonry class
31964  * 
31965  * @constructor
31966  * Create a new Element
31967  * @param {Object} config The config object
31968  */
31969
31970 Roo.bootstrap.LayoutMasonryAuto = function(config){
31971     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31972 };
31973
31974 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31975     
31976       /**
31977      * @cfg {Boolean} isFitWidth  - resize the width..
31978      */   
31979     isFitWidth : false,  // options..
31980     /**
31981      * @cfg {Boolean} isOriginLeft = left align?
31982      */   
31983     isOriginLeft : true,
31984     /**
31985      * @cfg {Boolean} isOriginTop = top align?
31986      */   
31987     isOriginTop : false,
31988     /**
31989      * @cfg {Boolean} isLayoutInstant = no animation?
31990      */   
31991     isLayoutInstant : false, // needed?
31992     /**
31993      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31994      */   
31995     isResizingContainer : true,
31996     /**
31997      * @cfg {Number} columnWidth  width of the columns 
31998      */   
31999     
32000     columnWidth : 0,
32001     
32002     /**
32003      * @cfg {Number} maxCols maximum number of columns
32004      */   
32005     
32006     maxCols: 0,
32007     /**
32008      * @cfg {Number} padHeight padding below box..
32009      */   
32010     
32011     padHeight : 10, 
32012     
32013     /**
32014      * @cfg {Boolean} isAutoInitial defalut true
32015      */   
32016     
32017     isAutoInitial : true, 
32018     
32019     // private?
32020     gutter : 0,
32021     
32022     containerWidth: 0,
32023     initialColumnWidth : 0,
32024     currentSize : null,
32025     
32026     colYs : null, // array.
32027     maxY : 0,
32028     padWidth: 10,
32029     
32030     
32031     tag: 'div',
32032     cls: '',
32033     bricks: null, //CompositeElement
32034     cols : 0, // array?
32035     // element : null, // wrapped now this.el
32036     _isLayoutInited : null, 
32037     
32038     
32039     getAutoCreate : function(){
32040         
32041         var cfg = {
32042             tag: this.tag,
32043             cls: 'blog-masonary-wrapper ' + this.cls,
32044             cn : {
32045                 cls : 'mas-boxes masonary'
32046             }
32047         };
32048         
32049         return cfg;
32050     },
32051     
32052     getChildContainer: function( )
32053     {
32054         if (this.boxesEl) {
32055             return this.boxesEl;
32056         }
32057         
32058         this.boxesEl = this.el.select('.mas-boxes').first();
32059         
32060         return this.boxesEl;
32061     },
32062     
32063     
32064     initEvents : function()
32065     {
32066         var _this = this;
32067         
32068         if(this.isAutoInitial){
32069             Roo.log('hook children rendered');
32070             this.on('childrenrendered', function() {
32071                 Roo.log('children rendered');
32072                 _this.initial();
32073             } ,this);
32074         }
32075         
32076     },
32077     
32078     initial : function()
32079     {
32080         this.reloadItems();
32081
32082         this.currentSize = this.el.getBox(true);
32083
32084         /// was window resize... - let's see if this works..
32085         Roo.EventManager.onWindowResize(this.resize, this); 
32086
32087         if(!this.isAutoInitial){
32088             this.layout();
32089             return;
32090         }
32091         
32092         this.layout.defer(500,this);
32093     },
32094     
32095     reloadItems: function()
32096     {
32097         this.bricks = this.el.select('.masonry-brick', true);
32098         
32099         this.bricks.each(function(b) {
32100             //Roo.log(b.getSize());
32101             if (!b.attr('originalwidth')) {
32102                 b.attr('originalwidth',  b.getSize().width);
32103             }
32104             
32105         });
32106         
32107         Roo.log(this.bricks.elements.length);
32108     },
32109     
32110     resize : function()
32111     {
32112         Roo.log('resize');
32113         var cs = this.el.getBox(true);
32114         
32115         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32116             Roo.log("no change in with or X");
32117             return;
32118         }
32119         this.currentSize = cs;
32120         this.layout();
32121     },
32122     
32123     layout : function()
32124     {
32125          Roo.log('layout');
32126         this._resetLayout();
32127         //this._manageStamps();
32128       
32129         // don't animate first layout
32130         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32131         this.layoutItems( isInstant );
32132       
32133         // flag for initalized
32134         this._isLayoutInited = true;
32135     },
32136     
32137     layoutItems : function( isInstant )
32138     {
32139         //var items = this._getItemsForLayout( this.items );
32140         // original code supports filtering layout items.. we just ignore it..
32141         
32142         this._layoutItems( this.bricks , isInstant );
32143       
32144         this._postLayout();
32145     },
32146     _layoutItems : function ( items , isInstant)
32147     {
32148        //this.fireEvent( 'layout', this, items );
32149     
32150
32151         if ( !items || !items.elements.length ) {
32152           // no items, emit event with empty array
32153             return;
32154         }
32155
32156         var queue = [];
32157         items.each(function(item) {
32158             Roo.log("layout item");
32159             Roo.log(item);
32160             // get x/y object from method
32161             var position = this._getItemLayoutPosition( item );
32162             // enqueue
32163             position.item = item;
32164             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32165             queue.push( position );
32166         }, this);
32167       
32168         this._processLayoutQueue( queue );
32169     },
32170     /** Sets position of item in DOM
32171     * @param {Element} item
32172     * @param {Number} x - horizontal position
32173     * @param {Number} y - vertical position
32174     * @param {Boolean} isInstant - disables transitions
32175     */
32176     _processLayoutQueue : function( queue )
32177     {
32178         for ( var i=0, len = queue.length; i < len; i++ ) {
32179             var obj = queue[i];
32180             obj.item.position('absolute');
32181             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32182         }
32183     },
32184       
32185     
32186     /**
32187     * Any logic you want to do after each layout,
32188     * i.e. size the container
32189     */
32190     _postLayout : function()
32191     {
32192         this.resizeContainer();
32193     },
32194     
32195     resizeContainer : function()
32196     {
32197         if ( !this.isResizingContainer ) {
32198             return;
32199         }
32200         var size = this._getContainerSize();
32201         if ( size ) {
32202             this.el.setSize(size.width,size.height);
32203             this.boxesEl.setSize(size.width,size.height);
32204         }
32205     },
32206     
32207     
32208     
32209     _resetLayout : function()
32210     {
32211         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32212         this.colWidth = this.el.getWidth();
32213         //this.gutter = this.el.getWidth(); 
32214         
32215         this.measureColumns();
32216
32217         // reset column Y
32218         var i = this.cols;
32219         this.colYs = [];
32220         while (i--) {
32221             this.colYs.push( 0 );
32222         }
32223     
32224         this.maxY = 0;
32225     },
32226
32227     measureColumns : function()
32228     {
32229         this.getContainerWidth();
32230       // if columnWidth is 0, default to outerWidth of first item
32231         if ( !this.columnWidth ) {
32232             var firstItem = this.bricks.first();
32233             Roo.log(firstItem);
32234             this.columnWidth  = this.containerWidth;
32235             if (firstItem && firstItem.attr('originalwidth') ) {
32236                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32237             }
32238             // columnWidth fall back to item of first element
32239             Roo.log("set column width?");
32240                         this.initialColumnWidth = this.columnWidth  ;
32241
32242             // if first elem has no width, default to size of container
32243             
32244         }
32245         
32246         
32247         if (this.initialColumnWidth) {
32248             this.columnWidth = this.initialColumnWidth;
32249         }
32250         
32251         
32252             
32253         // column width is fixed at the top - however if container width get's smaller we should
32254         // reduce it...
32255         
32256         // this bit calcs how man columns..
32257             
32258         var columnWidth = this.columnWidth += this.gutter;
32259       
32260         // calculate columns
32261         var containerWidth = this.containerWidth + this.gutter;
32262         
32263         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32264         // fix rounding errors, typically with gutters
32265         var excess = columnWidth - containerWidth % columnWidth;
32266         
32267         
32268         // if overshoot is less than a pixel, round up, otherwise floor it
32269         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32270         cols = Math[ mathMethod ]( cols );
32271         this.cols = Math.max( cols, 1 );
32272         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32273         
32274          // padding positioning..
32275         var totalColWidth = this.cols * this.columnWidth;
32276         var padavail = this.containerWidth - totalColWidth;
32277         // so for 2 columns - we need 3 'pads'
32278         
32279         var padNeeded = (1+this.cols) * this.padWidth;
32280         
32281         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32282         
32283         this.columnWidth += padExtra
32284         //this.padWidth = Math.floor(padavail /  ( this.cols));
32285         
32286         // adjust colum width so that padding is fixed??
32287         
32288         // we have 3 columns ... total = width * 3
32289         // we have X left over... that should be used by 
32290         
32291         //if (this.expandC) {
32292             
32293         //}
32294         
32295         
32296         
32297     },
32298     
32299     getContainerWidth : function()
32300     {
32301        /* // container is parent if fit width
32302         var container = this.isFitWidth ? this.element.parentNode : this.element;
32303         // check that this.size and size are there
32304         // IE8 triggers resize on body size change, so they might not be
32305         
32306         var size = getSize( container );  //FIXME
32307         this.containerWidth = size && size.innerWidth; //FIXME
32308         */
32309          
32310         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32311         
32312     },
32313     
32314     _getItemLayoutPosition : function( item )  // what is item?
32315     {
32316         // we resize the item to our columnWidth..
32317       
32318         item.setWidth(this.columnWidth);
32319         item.autoBoxAdjust  = false;
32320         
32321         var sz = item.getSize();
32322  
32323         // how many columns does this brick span
32324         var remainder = this.containerWidth % this.columnWidth;
32325         
32326         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32327         // round if off by 1 pixel, otherwise use ceil
32328         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32329         colSpan = Math.min( colSpan, this.cols );
32330         
32331         // normally this should be '1' as we dont' currently allow multi width columns..
32332         
32333         var colGroup = this._getColGroup( colSpan );
32334         // get the minimum Y value from the columns
32335         var minimumY = Math.min.apply( Math, colGroup );
32336         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32337         
32338         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32339          
32340         // position the brick
32341         var position = {
32342             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32343             y: this.currentSize.y + minimumY + this.padHeight
32344         };
32345         
32346         Roo.log(position);
32347         // apply setHeight to necessary columns
32348         var setHeight = minimumY + sz.height + this.padHeight;
32349         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32350         
32351         var setSpan = this.cols + 1 - colGroup.length;
32352         for ( var i = 0; i < setSpan; i++ ) {
32353           this.colYs[ shortColIndex + i ] = setHeight ;
32354         }
32355       
32356         return position;
32357     },
32358     
32359     /**
32360      * @param {Number} colSpan - number of columns the element spans
32361      * @returns {Array} colGroup
32362      */
32363     _getColGroup : function( colSpan )
32364     {
32365         if ( colSpan < 2 ) {
32366           // if brick spans only one column, use all the column Ys
32367           return this.colYs;
32368         }
32369       
32370         var colGroup = [];
32371         // how many different places could this brick fit horizontally
32372         var groupCount = this.cols + 1 - colSpan;
32373         // for each group potential horizontal position
32374         for ( var i = 0; i < groupCount; i++ ) {
32375           // make an array of colY values for that one group
32376           var groupColYs = this.colYs.slice( i, i + colSpan );
32377           // and get the max value of the array
32378           colGroup[i] = Math.max.apply( Math, groupColYs );
32379         }
32380         return colGroup;
32381     },
32382     /*
32383     _manageStamp : function( stamp )
32384     {
32385         var stampSize =  stamp.getSize();
32386         var offset = stamp.getBox();
32387         // get the columns that this stamp affects
32388         var firstX = this.isOriginLeft ? offset.x : offset.right;
32389         var lastX = firstX + stampSize.width;
32390         var firstCol = Math.floor( firstX / this.columnWidth );
32391         firstCol = Math.max( 0, firstCol );
32392         
32393         var lastCol = Math.floor( lastX / this.columnWidth );
32394         // lastCol should not go over if multiple of columnWidth #425
32395         lastCol -= lastX % this.columnWidth ? 0 : 1;
32396         lastCol = Math.min( this.cols - 1, lastCol );
32397         
32398         // set colYs to bottom of the stamp
32399         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32400             stampSize.height;
32401             
32402         for ( var i = firstCol; i <= lastCol; i++ ) {
32403           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32404         }
32405     },
32406     */
32407     
32408     _getContainerSize : function()
32409     {
32410         this.maxY = Math.max.apply( Math, this.colYs );
32411         var size = {
32412             height: this.maxY
32413         };
32414       
32415         if ( this.isFitWidth ) {
32416             size.width = this._getContainerFitWidth();
32417         }
32418       
32419         return size;
32420     },
32421     
32422     _getContainerFitWidth : function()
32423     {
32424         var unusedCols = 0;
32425         // count unused columns
32426         var i = this.cols;
32427         while ( --i ) {
32428           if ( this.colYs[i] !== 0 ) {
32429             break;
32430           }
32431           unusedCols++;
32432         }
32433         // fit container to columns that have been used
32434         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32435     },
32436     
32437     needsResizeLayout : function()
32438     {
32439         var previousWidth = this.containerWidth;
32440         this.getContainerWidth();
32441         return previousWidth !== this.containerWidth;
32442     }
32443  
32444 });
32445
32446  
32447
32448  /*
32449  * - LGPL
32450  *
32451  * element
32452  * 
32453  */
32454
32455 /**
32456  * @class Roo.bootstrap.MasonryBrick
32457  * @extends Roo.bootstrap.Component
32458  * Bootstrap MasonryBrick class
32459  * 
32460  * @constructor
32461  * Create a new MasonryBrick
32462  * @param {Object} config The config object
32463  */
32464
32465 Roo.bootstrap.MasonryBrick = function(config){
32466     
32467     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32468     
32469     Roo.bootstrap.MasonryBrick.register(this);
32470     
32471     this.addEvents({
32472         // raw events
32473         /**
32474          * @event click
32475          * When a MasonryBrick is clcik
32476          * @param {Roo.bootstrap.MasonryBrick} this
32477          * @param {Roo.EventObject} e
32478          */
32479         "click" : true
32480     });
32481 };
32482
32483 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32484     
32485     /**
32486      * @cfg {String} title
32487      */   
32488     title : '',
32489     /**
32490      * @cfg {String} html
32491      */   
32492     html : '',
32493     /**
32494      * @cfg {String} bgimage
32495      */   
32496     bgimage : '',
32497     /**
32498      * @cfg {String} videourl
32499      */   
32500     videourl : '',
32501     /**
32502      * @cfg {String} cls
32503      */   
32504     cls : '',
32505     /**
32506      * @cfg {String} href
32507      */   
32508     href : '',
32509     /**
32510      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32511      */   
32512     size : 'xs',
32513     
32514     /**
32515      * @cfg {String} placetitle (center|bottom)
32516      */   
32517     placetitle : '',
32518     
32519     /**
32520      * @cfg {Boolean} isFitContainer defalut true
32521      */   
32522     isFitContainer : true, 
32523     
32524     /**
32525      * @cfg {Boolean} preventDefault defalut false
32526      */   
32527     preventDefault : false, 
32528     
32529     /**
32530      * @cfg {Boolean} inverse defalut false
32531      */   
32532     maskInverse : false, 
32533     
32534     getAutoCreate : function()
32535     {
32536         if(!this.isFitContainer){
32537             return this.getSplitAutoCreate();
32538         }
32539         
32540         var cls = 'masonry-brick masonry-brick-full';
32541         
32542         if(this.href.length){
32543             cls += ' masonry-brick-link';
32544         }
32545         
32546         if(this.bgimage.length){
32547             cls += ' masonry-brick-image';
32548         }
32549         
32550         if(this.maskInverse){
32551             cls += ' mask-inverse';
32552         }
32553         
32554         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32555             cls += ' enable-mask';
32556         }
32557         
32558         if(this.size){
32559             cls += ' masonry-' + this.size + '-brick';
32560         }
32561         
32562         if(this.placetitle.length){
32563             
32564             switch (this.placetitle) {
32565                 case 'center' :
32566                     cls += ' masonry-center-title';
32567                     break;
32568                 case 'bottom' :
32569                     cls += ' masonry-bottom-title';
32570                     break;
32571                 default:
32572                     break;
32573             }
32574             
32575         } else {
32576             if(!this.html.length && !this.bgimage.length){
32577                 cls += ' masonry-center-title';
32578             }
32579
32580             if(!this.html.length && this.bgimage.length){
32581                 cls += ' masonry-bottom-title';
32582             }
32583         }
32584         
32585         if(this.cls){
32586             cls += ' ' + this.cls;
32587         }
32588         
32589         var cfg = {
32590             tag: (this.href.length) ? 'a' : 'div',
32591             cls: cls,
32592             cn: [
32593                 {
32594                     tag: 'div',
32595                     cls: 'masonry-brick-mask'
32596                 },
32597                 {
32598                     tag: 'div',
32599                     cls: 'masonry-brick-paragraph',
32600                     cn: []
32601                 }
32602             ]
32603         };
32604         
32605         if(this.href.length){
32606             cfg.href = this.href;
32607         }
32608         
32609         var cn = cfg.cn[1].cn;
32610         
32611         if(this.title.length){
32612             cn.push({
32613                 tag: 'h4',
32614                 cls: 'masonry-brick-title',
32615                 html: this.title
32616             });
32617         }
32618         
32619         if(this.html.length){
32620             cn.push({
32621                 tag: 'p',
32622                 cls: 'masonry-brick-text',
32623                 html: this.html
32624             });
32625         }
32626         
32627         if (!this.title.length && !this.html.length) {
32628             cfg.cn[1].cls += ' hide';
32629         }
32630         
32631         if(this.bgimage.length){
32632             cfg.cn.push({
32633                 tag: 'img',
32634                 cls: 'masonry-brick-image-view',
32635                 src: this.bgimage
32636             });
32637         }
32638         
32639         if(this.videourl.length){
32640             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32641             // youtube support only?
32642             cfg.cn.push({
32643                 tag: 'iframe',
32644                 cls: 'masonry-brick-image-view',
32645                 src: vurl,
32646                 frameborder : 0,
32647                 allowfullscreen : true
32648             });
32649         }
32650         
32651         return cfg;
32652         
32653     },
32654     
32655     getSplitAutoCreate : function()
32656     {
32657         var cls = 'masonry-brick masonry-brick-split';
32658         
32659         if(this.href.length){
32660             cls += ' masonry-brick-link';
32661         }
32662         
32663         if(this.bgimage.length){
32664             cls += ' masonry-brick-image';
32665         }
32666         
32667         if(this.size){
32668             cls += ' masonry-' + this.size + '-brick';
32669         }
32670         
32671         switch (this.placetitle) {
32672             case 'center' :
32673                 cls += ' masonry-center-title';
32674                 break;
32675             case 'bottom' :
32676                 cls += ' masonry-bottom-title';
32677                 break;
32678             default:
32679                 if(!this.bgimage.length){
32680                     cls += ' masonry-center-title';
32681                 }
32682
32683                 if(this.bgimage.length){
32684                     cls += ' masonry-bottom-title';
32685                 }
32686                 break;
32687         }
32688         
32689         if(this.cls){
32690             cls += ' ' + this.cls;
32691         }
32692         
32693         var cfg = {
32694             tag: (this.href.length) ? 'a' : 'div',
32695             cls: cls,
32696             cn: [
32697                 {
32698                     tag: 'div',
32699                     cls: 'masonry-brick-split-head',
32700                     cn: [
32701                         {
32702                             tag: 'div',
32703                             cls: 'masonry-brick-paragraph',
32704                             cn: []
32705                         }
32706                     ]
32707                 },
32708                 {
32709                     tag: 'div',
32710                     cls: 'masonry-brick-split-body',
32711                     cn: []
32712                 }
32713             ]
32714         };
32715         
32716         if(this.href.length){
32717             cfg.href = this.href;
32718         }
32719         
32720         if(this.title.length){
32721             cfg.cn[0].cn[0].cn.push({
32722                 tag: 'h4',
32723                 cls: 'masonry-brick-title',
32724                 html: this.title
32725             });
32726         }
32727         
32728         if(this.html.length){
32729             cfg.cn[1].cn.push({
32730                 tag: 'p',
32731                 cls: 'masonry-brick-text',
32732                 html: this.html
32733             });
32734         }
32735
32736         if(this.bgimage.length){
32737             cfg.cn[0].cn.push({
32738                 tag: 'img',
32739                 cls: 'masonry-brick-image-view',
32740                 src: this.bgimage
32741             });
32742         }
32743         
32744         if(this.videourl.length){
32745             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32746             // youtube support only?
32747             cfg.cn[0].cn.cn.push({
32748                 tag: 'iframe',
32749                 cls: 'masonry-brick-image-view',
32750                 src: vurl,
32751                 frameborder : 0,
32752                 allowfullscreen : true
32753             });
32754         }
32755         
32756         return cfg;
32757     },
32758     
32759     initEvents: function() 
32760     {
32761         switch (this.size) {
32762             case 'xs' :
32763                 this.x = 1;
32764                 this.y = 1;
32765                 break;
32766             case 'sm' :
32767                 this.x = 2;
32768                 this.y = 2;
32769                 break;
32770             case 'md' :
32771             case 'md-left' :
32772             case 'md-right' :
32773                 this.x = 3;
32774                 this.y = 3;
32775                 break;
32776             case 'tall' :
32777                 this.x = 2;
32778                 this.y = 3;
32779                 break;
32780             case 'wide' :
32781                 this.x = 3;
32782                 this.y = 2;
32783                 break;
32784             case 'wide-thin' :
32785                 this.x = 3;
32786                 this.y = 1;
32787                 break;
32788                         
32789             default :
32790                 break;
32791         }
32792         
32793         if(Roo.isTouch){
32794             this.el.on('touchstart', this.onTouchStart, this);
32795             this.el.on('touchmove', this.onTouchMove, this);
32796             this.el.on('touchend', this.onTouchEnd, this);
32797             this.el.on('contextmenu', this.onContextMenu, this);
32798         } else {
32799             this.el.on('mouseenter'  ,this.enter, this);
32800             this.el.on('mouseleave', this.leave, this);
32801             this.el.on('click', this.onClick, this);
32802         }
32803         
32804         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32805             this.parent().bricks.push(this);   
32806         }
32807         
32808     },
32809     
32810     onClick: function(e, el)
32811     {
32812         var time = this.endTimer - this.startTimer;
32813         // Roo.log(e.preventDefault());
32814         if(Roo.isTouch){
32815             if(time > 1000){
32816                 e.preventDefault();
32817                 return;
32818             }
32819         }
32820         
32821         if(!this.preventDefault){
32822             return;
32823         }
32824         
32825         e.preventDefault();
32826         
32827         if (this.activeClass != '') {
32828             this.selectBrick();
32829         }
32830         
32831         this.fireEvent('click', this, e);
32832     },
32833     
32834     enter: function(e, el)
32835     {
32836         e.preventDefault();
32837         
32838         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32839             return;
32840         }
32841         
32842         if(this.bgimage.length && this.html.length){
32843             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32844         }
32845     },
32846     
32847     leave: function(e, el)
32848     {
32849         e.preventDefault();
32850         
32851         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32852             return;
32853         }
32854         
32855         if(this.bgimage.length && this.html.length){
32856             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32857         }
32858     },
32859     
32860     onTouchStart: function(e, el)
32861     {
32862 //        e.preventDefault();
32863         
32864         this.touchmoved = false;
32865         
32866         if(!this.isFitContainer){
32867             return;
32868         }
32869         
32870         if(!this.bgimage.length || !this.html.length){
32871             return;
32872         }
32873         
32874         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32875         
32876         this.timer = new Date().getTime();
32877         
32878     },
32879     
32880     onTouchMove: function(e, el)
32881     {
32882         this.touchmoved = true;
32883     },
32884     
32885     onContextMenu : function(e,el)
32886     {
32887         e.preventDefault();
32888         e.stopPropagation();
32889         return false;
32890     },
32891     
32892     onTouchEnd: function(e, el)
32893     {
32894 //        e.preventDefault();
32895         
32896         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32897         
32898             this.leave(e,el);
32899             
32900             return;
32901         }
32902         
32903         if(!this.bgimage.length || !this.html.length){
32904             
32905             if(this.href.length){
32906                 window.location.href = this.href;
32907             }
32908             
32909             return;
32910         }
32911         
32912         if(!this.isFitContainer){
32913             return;
32914         }
32915         
32916         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32917         
32918         window.location.href = this.href;
32919     },
32920     
32921     //selection on single brick only
32922     selectBrick : function() {
32923         
32924         if (!this.parentId) {
32925             return;
32926         }
32927         
32928         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32929         var index = m.selectedBrick.indexOf(this.id);
32930         
32931         if ( index > -1) {
32932             m.selectedBrick.splice(index,1);
32933             this.el.removeClass(this.activeClass);
32934             return;
32935         }
32936         
32937         for(var i = 0; i < m.selectedBrick.length; i++) {
32938             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32939             b.el.removeClass(b.activeClass);
32940         }
32941         
32942         m.selectedBrick = [];
32943         
32944         m.selectedBrick.push(this.id);
32945         this.el.addClass(this.activeClass);
32946         return;
32947     },
32948     
32949     isSelected : function(){
32950         return this.el.hasClass(this.activeClass);
32951         
32952     }
32953 });
32954
32955 Roo.apply(Roo.bootstrap.MasonryBrick, {
32956     
32957     //groups: {},
32958     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32959      /**
32960     * register a Masonry Brick
32961     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32962     */
32963     
32964     register : function(brick)
32965     {
32966         //this.groups[brick.id] = brick;
32967         this.groups.add(brick.id, brick);
32968     },
32969     /**
32970     * fetch a  masonry brick based on the masonry brick ID
32971     * @param {string} the masonry brick to add
32972     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32973     */
32974     
32975     get: function(brick_id) 
32976     {
32977         // if (typeof(this.groups[brick_id]) == 'undefined') {
32978         //     return false;
32979         // }
32980         // return this.groups[brick_id] ;
32981         
32982         if(this.groups.key(brick_id)) {
32983             return this.groups.key(brick_id);
32984         }
32985         
32986         return false;
32987     }
32988     
32989     
32990     
32991 });
32992
32993  /*
32994  * - LGPL
32995  *
32996  * element
32997  * 
32998  */
32999
33000 /**
33001  * @class Roo.bootstrap.Brick
33002  * @extends Roo.bootstrap.Component
33003  * Bootstrap Brick class
33004  * 
33005  * @constructor
33006  * Create a new Brick
33007  * @param {Object} config The config object
33008  */
33009
33010 Roo.bootstrap.Brick = function(config){
33011     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33012     
33013     this.addEvents({
33014         // raw events
33015         /**
33016          * @event click
33017          * When a Brick is click
33018          * @param {Roo.bootstrap.Brick} this
33019          * @param {Roo.EventObject} e
33020          */
33021         "click" : true
33022     });
33023 };
33024
33025 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33026     
33027     /**
33028      * @cfg {String} title
33029      */   
33030     title : '',
33031     /**
33032      * @cfg {String} html
33033      */   
33034     html : '',
33035     /**
33036      * @cfg {String} bgimage
33037      */   
33038     bgimage : '',
33039     /**
33040      * @cfg {String} cls
33041      */   
33042     cls : '',
33043     /**
33044      * @cfg {String} href
33045      */   
33046     href : '',
33047     /**
33048      * @cfg {String} video
33049      */   
33050     video : '',
33051     /**
33052      * @cfg {Boolean} square
33053      */   
33054     square : true,
33055     
33056     getAutoCreate : function()
33057     {
33058         var cls = 'roo-brick';
33059         
33060         if(this.href.length){
33061             cls += ' roo-brick-link';
33062         }
33063         
33064         if(this.bgimage.length){
33065             cls += ' roo-brick-image';
33066         }
33067         
33068         if(!this.html.length && !this.bgimage.length){
33069             cls += ' roo-brick-center-title';
33070         }
33071         
33072         if(!this.html.length && this.bgimage.length){
33073             cls += ' roo-brick-bottom-title';
33074         }
33075         
33076         if(this.cls){
33077             cls += ' ' + this.cls;
33078         }
33079         
33080         var cfg = {
33081             tag: (this.href.length) ? 'a' : 'div',
33082             cls: cls,
33083             cn: [
33084                 {
33085                     tag: 'div',
33086                     cls: 'roo-brick-paragraph',
33087                     cn: []
33088                 }
33089             ]
33090         };
33091         
33092         if(this.href.length){
33093             cfg.href = this.href;
33094         }
33095         
33096         var cn = cfg.cn[0].cn;
33097         
33098         if(this.title.length){
33099             cn.push({
33100                 tag: 'h4',
33101                 cls: 'roo-brick-title',
33102                 html: this.title
33103             });
33104         }
33105         
33106         if(this.html.length){
33107             cn.push({
33108                 tag: 'p',
33109                 cls: 'roo-brick-text',
33110                 html: this.html
33111             });
33112         } else {
33113             cn.cls += ' hide';
33114         }
33115         
33116         if(this.bgimage.length){
33117             cfg.cn.push({
33118                 tag: 'img',
33119                 cls: 'roo-brick-image-view',
33120                 src: this.bgimage
33121             });
33122         }
33123         
33124         return cfg;
33125     },
33126     
33127     initEvents: function() 
33128     {
33129         if(this.title.length || this.html.length){
33130             this.el.on('mouseenter'  ,this.enter, this);
33131             this.el.on('mouseleave', this.leave, this);
33132         }
33133         
33134         Roo.EventManager.onWindowResize(this.resize, this); 
33135         
33136         if(this.bgimage.length){
33137             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33138             this.imageEl.on('load', this.onImageLoad, this);
33139             return;
33140         }
33141         
33142         this.resize();
33143     },
33144     
33145     onImageLoad : function()
33146     {
33147         this.resize();
33148     },
33149     
33150     resize : function()
33151     {
33152         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33153         
33154         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33155         
33156         if(this.bgimage.length){
33157             var image = this.el.select('.roo-brick-image-view', true).first();
33158             
33159             image.setWidth(paragraph.getWidth());
33160             
33161             if(this.square){
33162                 image.setHeight(paragraph.getWidth());
33163             }
33164             
33165             this.el.setHeight(image.getHeight());
33166             paragraph.setHeight(image.getHeight());
33167             
33168         }
33169         
33170     },
33171     
33172     enter: function(e, el)
33173     {
33174         e.preventDefault();
33175         
33176         if(this.bgimage.length){
33177             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33178             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33179         }
33180     },
33181     
33182     leave: function(e, el)
33183     {
33184         e.preventDefault();
33185         
33186         if(this.bgimage.length){
33187             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33188             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33189         }
33190     }
33191     
33192 });
33193
33194  
33195
33196  /*
33197  * - LGPL
33198  *
33199  * Number field 
33200  */
33201
33202 /**
33203  * @class Roo.bootstrap.NumberField
33204  * @extends Roo.bootstrap.Input
33205  * Bootstrap NumberField class
33206  * 
33207  * 
33208  * 
33209  * 
33210  * @constructor
33211  * Create a new NumberField
33212  * @param {Object} config The config object
33213  */
33214
33215 Roo.bootstrap.NumberField = function(config){
33216     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33217 };
33218
33219 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33220     
33221     /**
33222      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33223      */
33224     allowDecimals : true,
33225     /**
33226      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33227      */
33228     decimalSeparator : ".",
33229     /**
33230      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33231      */
33232     decimalPrecision : 2,
33233     /**
33234      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33235      */
33236     allowNegative : true,
33237     
33238     /**
33239      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33240      */
33241     allowZero: true,
33242     /**
33243      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33244      */
33245     minValue : Number.NEGATIVE_INFINITY,
33246     /**
33247      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33248      */
33249     maxValue : Number.MAX_VALUE,
33250     /**
33251      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33252      */
33253     minText : "The minimum value for this field is {0}",
33254     /**
33255      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33256      */
33257     maxText : "The maximum value for this field is {0}",
33258     /**
33259      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33260      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33261      */
33262     nanText : "{0} is not a valid number",
33263     /**
33264      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33265      */
33266     thousandsDelimiter : false,
33267     /**
33268      * @cfg {String} valueAlign alignment of value
33269      */
33270     valueAlign : "left",
33271
33272     getAutoCreate : function()
33273     {
33274         var hiddenInput = {
33275             tag: 'input',
33276             type: 'hidden',
33277             id: Roo.id(),
33278             cls: 'hidden-number-input'
33279         };
33280         
33281         if (this.name) {
33282             hiddenInput.name = this.name;
33283         }
33284         
33285         this.name = '';
33286         
33287         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33288         
33289         this.name = hiddenInput.name;
33290         
33291         if(cfg.cn.length > 0) {
33292             cfg.cn.push(hiddenInput);
33293         }
33294         
33295         return cfg;
33296     },
33297
33298     // private
33299     initEvents : function()
33300     {   
33301         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33302         
33303         var allowed = "0123456789";
33304         
33305         if(this.allowDecimals){
33306             allowed += this.decimalSeparator;
33307         }
33308         
33309         if(this.allowNegative){
33310             allowed += "-";
33311         }
33312         
33313         if(this.thousandsDelimiter) {
33314             allowed += ",";
33315         }
33316         
33317         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33318         
33319         var keyPress = function(e){
33320             
33321             var k = e.getKey();
33322             
33323             var c = e.getCharCode();
33324             
33325             if(
33326                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33327                     allowed.indexOf(String.fromCharCode(c)) === -1
33328             ){
33329                 e.stopEvent();
33330                 return;
33331             }
33332             
33333             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33334                 return;
33335             }
33336             
33337             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33338                 e.stopEvent();
33339             }
33340         };
33341         
33342         this.el.on("keypress", keyPress, this);
33343     },
33344     
33345     validateValue : function(value)
33346     {
33347         
33348         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33349             return false;
33350         }
33351         
33352         var num = this.parseValue(value);
33353         
33354         if(isNaN(num)){
33355             this.markInvalid(String.format(this.nanText, value));
33356             return false;
33357         }
33358         
33359         if(num < this.minValue){
33360             this.markInvalid(String.format(this.minText, this.minValue));
33361             return false;
33362         }
33363         
33364         if(num > this.maxValue){
33365             this.markInvalid(String.format(this.maxText, this.maxValue));
33366             return false;
33367         }
33368         
33369         return true;
33370     },
33371
33372     getValue : function()
33373     {
33374         var v = this.hiddenEl().getValue();
33375         
33376         return this.fixPrecision(this.parseValue(v));
33377     },
33378
33379     parseValue : function(value)
33380     {
33381         if(this.thousandsDelimiter) {
33382             value += "";
33383             r = new RegExp(",", "g");
33384             value = value.replace(r, "");
33385         }
33386         
33387         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33388         return isNaN(value) ? '' : value;
33389     },
33390
33391     fixPrecision : function(value)
33392     {
33393         if(this.thousandsDelimiter) {
33394             value += "";
33395             r = new RegExp(",", "g");
33396             value = value.replace(r, "");
33397         }
33398         
33399         var nan = isNaN(value);
33400         
33401         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33402             return nan ? '' : value;
33403         }
33404         return parseFloat(value).toFixed(this.decimalPrecision);
33405     },
33406
33407     setValue : function(v)
33408     {
33409         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33410         
33411         this.value = v;
33412         
33413         if(this.rendered){
33414             
33415             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33416             
33417             this.inputEl().dom.value = (v == '') ? '' :
33418                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33419             
33420             if(!this.allowZero && v === '0') {
33421                 this.hiddenEl().dom.value = '';
33422                 this.inputEl().dom.value = '';
33423             }
33424             
33425             this.validate();
33426         }
33427     },
33428
33429     decimalPrecisionFcn : function(v)
33430     {
33431         return Math.floor(v);
33432     },
33433
33434     beforeBlur : function()
33435     {
33436         var v = this.parseValue(this.getRawValue());
33437         
33438         if(v || v === 0 || v === ''){
33439             this.setValue(v);
33440         }
33441     },
33442     
33443     hiddenEl : function()
33444     {
33445         return this.el.select('input.hidden-number-input',true).first();
33446     }
33447     
33448 });
33449
33450  
33451
33452 /*
33453 * Licence: LGPL
33454 */
33455
33456 /**
33457  * @class Roo.bootstrap.DocumentSlider
33458  * @extends Roo.bootstrap.Component
33459  * Bootstrap DocumentSlider class
33460  * 
33461  * @constructor
33462  * Create a new DocumentViewer
33463  * @param {Object} config The config object
33464  */
33465
33466 Roo.bootstrap.DocumentSlider = function(config){
33467     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33468     
33469     this.files = [];
33470     
33471     this.addEvents({
33472         /**
33473          * @event initial
33474          * Fire after initEvent
33475          * @param {Roo.bootstrap.DocumentSlider} this
33476          */
33477         "initial" : true,
33478         /**
33479          * @event update
33480          * Fire after update
33481          * @param {Roo.bootstrap.DocumentSlider} this
33482          */
33483         "update" : true,
33484         /**
33485          * @event click
33486          * Fire after click
33487          * @param {Roo.bootstrap.DocumentSlider} this
33488          */
33489         "click" : true
33490     });
33491 };
33492
33493 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33494     
33495     files : false,
33496     
33497     indicator : 0,
33498     
33499     getAutoCreate : function()
33500     {
33501         var cfg = {
33502             tag : 'div',
33503             cls : 'roo-document-slider',
33504             cn : [
33505                 {
33506                     tag : 'div',
33507                     cls : 'roo-document-slider-header',
33508                     cn : [
33509                         {
33510                             tag : 'div',
33511                             cls : 'roo-document-slider-header-title'
33512                         }
33513                     ]
33514                 },
33515                 {
33516                     tag : 'div',
33517                     cls : 'roo-document-slider-body',
33518                     cn : [
33519                         {
33520                             tag : 'div',
33521                             cls : 'roo-document-slider-prev',
33522                             cn : [
33523                                 {
33524                                     tag : 'i',
33525                                     cls : 'fa fa-chevron-left'
33526                                 }
33527                             ]
33528                         },
33529                         {
33530                             tag : 'div',
33531                             cls : 'roo-document-slider-thumb',
33532                             cn : [
33533                                 {
33534                                     tag : 'img',
33535                                     cls : 'roo-document-slider-image'
33536                                 }
33537                             ]
33538                         },
33539                         {
33540                             tag : 'div',
33541                             cls : 'roo-document-slider-next',
33542                             cn : [
33543                                 {
33544                                     tag : 'i',
33545                                     cls : 'fa fa-chevron-right'
33546                                 }
33547                             ]
33548                         }
33549                     ]
33550                 }
33551             ]
33552         };
33553         
33554         return cfg;
33555     },
33556     
33557     initEvents : function()
33558     {
33559         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33560         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33561         
33562         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33563         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33564         
33565         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33566         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33567         
33568         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33569         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33570         
33571         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33572         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33573         
33574         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33575         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33576         
33577         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33578         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33579         
33580         this.thumbEl.on('click', this.onClick, this);
33581         
33582         this.prevIndicator.on('click', this.prev, this);
33583         
33584         this.nextIndicator.on('click', this.next, this);
33585         
33586     },
33587     
33588     initial : function()
33589     {
33590         if(this.files.length){
33591             this.indicator = 1;
33592             this.update()
33593         }
33594         
33595         this.fireEvent('initial', this);
33596     },
33597     
33598     update : function()
33599     {
33600         this.imageEl.attr('src', this.files[this.indicator - 1]);
33601         
33602         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33603         
33604         this.prevIndicator.show();
33605         
33606         if(this.indicator == 1){
33607             this.prevIndicator.hide();
33608         }
33609         
33610         this.nextIndicator.show();
33611         
33612         if(this.indicator == this.files.length){
33613             this.nextIndicator.hide();
33614         }
33615         
33616         this.thumbEl.scrollTo('top');
33617         
33618         this.fireEvent('update', this);
33619     },
33620     
33621     onClick : function(e)
33622     {
33623         e.preventDefault();
33624         
33625         this.fireEvent('click', this);
33626     },
33627     
33628     prev : function(e)
33629     {
33630         e.preventDefault();
33631         
33632         this.indicator = Math.max(1, this.indicator - 1);
33633         
33634         this.update();
33635     },
33636     
33637     next : function(e)
33638     {
33639         e.preventDefault();
33640         
33641         this.indicator = Math.min(this.files.length, this.indicator + 1);
33642         
33643         this.update();
33644     }
33645 });
33646 /*
33647  * - LGPL
33648  *
33649  * RadioSet
33650  *
33651  *
33652  */
33653
33654 /**
33655  * @class Roo.bootstrap.RadioSet
33656  * @extends Roo.bootstrap.Input
33657  * Bootstrap RadioSet class
33658  * @cfg {String} indicatorpos (left|right) default left
33659  * @cfg {Boolean} inline (true|false) inline the element (default true)
33660  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33661  * @constructor
33662  * Create a new RadioSet
33663  * @param {Object} config The config object
33664  */
33665
33666 Roo.bootstrap.RadioSet = function(config){
33667     
33668     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33669     
33670     this.radioes = [];
33671     
33672     Roo.bootstrap.RadioSet.register(this);
33673     
33674     this.addEvents({
33675         /**
33676         * @event check
33677         * Fires when the element is checked or unchecked.
33678         * @param {Roo.bootstrap.RadioSet} this This radio
33679         * @param {Roo.bootstrap.Radio} item The checked item
33680         */
33681        check : true,
33682        /**
33683         * @event click
33684         * Fires when the element is click.
33685         * @param {Roo.bootstrap.RadioSet} this This radio set
33686         * @param {Roo.bootstrap.Radio} item The checked item
33687         * @param {Roo.EventObject} e The event object
33688         */
33689        click : true
33690     });
33691     
33692 };
33693
33694 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33695
33696     radioes : false,
33697     
33698     inline : true,
33699     
33700     weight : '',
33701     
33702     indicatorpos : 'left',
33703     
33704     getAutoCreate : function()
33705     {
33706         var label = {
33707             tag : 'label',
33708             cls : 'roo-radio-set-label',
33709             cn : [
33710                 {
33711                     tag : 'span',
33712                     html : this.fieldLabel
33713                 }
33714             ]
33715         };
33716         
33717         if(this.indicatorpos == 'left'){
33718             label.cn.unshift({
33719                 tag : 'i',
33720                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33721                 tooltip : 'This field is required'
33722             });
33723         } else {
33724             label.cn.push({
33725                 tag : 'i',
33726                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33727                 tooltip : 'This field is required'
33728             });
33729         }
33730         
33731         var items = {
33732             tag : 'div',
33733             cls : 'roo-radio-set-items'
33734         };
33735         
33736         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33737         
33738         if (align === 'left' && this.fieldLabel.length) {
33739             
33740             items = {
33741                 cls : "roo-radio-set-right", 
33742                 cn: [
33743                     items
33744                 ]
33745             };
33746             
33747             if(this.labelWidth > 12){
33748                 label.style = "width: " + this.labelWidth + 'px';
33749             }
33750             
33751             if(this.labelWidth < 13 && this.labelmd == 0){
33752                 this.labelmd = this.labelWidth;
33753             }
33754             
33755             if(this.labellg > 0){
33756                 label.cls += ' col-lg-' + this.labellg;
33757                 items.cls += ' col-lg-' + (12 - this.labellg);
33758             }
33759             
33760             if(this.labelmd > 0){
33761                 label.cls += ' col-md-' + this.labelmd;
33762                 items.cls += ' col-md-' + (12 - this.labelmd);
33763             }
33764             
33765             if(this.labelsm > 0){
33766                 label.cls += ' col-sm-' + this.labelsm;
33767                 items.cls += ' col-sm-' + (12 - this.labelsm);
33768             }
33769             
33770             if(this.labelxs > 0){
33771                 label.cls += ' col-xs-' + this.labelxs;
33772                 items.cls += ' col-xs-' + (12 - this.labelxs);
33773             }
33774         }
33775         
33776         var cfg = {
33777             tag : 'div',
33778             cls : 'roo-radio-set',
33779             cn : [
33780                 {
33781                     tag : 'input',
33782                     cls : 'roo-radio-set-input',
33783                     type : 'hidden',
33784                     name : this.name,
33785                     value : this.value ? this.value :  ''
33786                 },
33787                 label,
33788                 items
33789             ]
33790         };
33791         
33792         if(this.weight.length){
33793             cfg.cls += ' roo-radio-' + this.weight;
33794         }
33795         
33796         if(this.inline) {
33797             cfg.cls += ' roo-radio-set-inline';
33798         }
33799         
33800         var settings=this;
33801         ['xs','sm','md','lg'].map(function(size){
33802             if (settings[size]) {
33803                 cfg.cls += ' col-' + size + '-' + settings[size];
33804             }
33805         });
33806         
33807         return cfg;
33808         
33809     },
33810
33811     initEvents : function()
33812     {
33813         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33814         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33815         
33816         if(!this.fieldLabel.length){
33817             this.labelEl.hide();
33818         }
33819         
33820         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33821         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33822         
33823         this.indicator = this.indicatorEl();
33824         
33825         if(this.indicator){
33826             this.indicator.addClass('invisible');
33827         }
33828         
33829         this.originalValue = this.getValue();
33830         
33831     },
33832     
33833     inputEl: function ()
33834     {
33835         return this.el.select('.roo-radio-set-input', true).first();
33836     },
33837     
33838     getChildContainer : function()
33839     {
33840         return this.itemsEl;
33841     },
33842     
33843     register : function(item)
33844     {
33845         this.radioes.push(item);
33846         
33847     },
33848     
33849     validate : function()
33850     {   
33851         if(this.getVisibilityEl().hasClass('hidden')){
33852             return true;
33853         }
33854         
33855         var valid = false;
33856         
33857         Roo.each(this.radioes, function(i){
33858             if(!i.checked){
33859                 return;
33860             }
33861             
33862             valid = true;
33863             return false;
33864         });
33865         
33866         if(this.allowBlank) {
33867             return true;
33868         }
33869         
33870         if(this.disabled || valid){
33871             this.markValid();
33872             return true;
33873         }
33874         
33875         this.markInvalid();
33876         return false;
33877         
33878     },
33879     
33880     markValid : function()
33881     {
33882         if(this.labelEl.isVisible(true)){
33883             this.indicatorEl().removeClass('visible');
33884             this.indicatorEl().addClass('invisible');
33885         }
33886         
33887         this.el.removeClass([this.invalidClass, this.validClass]);
33888         this.el.addClass(this.validClass);
33889         
33890         this.fireEvent('valid', this);
33891     },
33892     
33893     markInvalid : function(msg)
33894     {
33895         if(this.allowBlank || this.disabled){
33896             return;
33897         }
33898         
33899         if(this.labelEl.isVisible(true)){
33900             this.indicatorEl().removeClass('invisible');
33901             this.indicatorEl().addClass('visible');
33902         }
33903         
33904         this.el.removeClass([this.invalidClass, this.validClass]);
33905         this.el.addClass(this.invalidClass);
33906         
33907         this.fireEvent('invalid', this, msg);
33908         
33909     },
33910     
33911     setValue : function(v, suppressEvent)
33912     {   
33913         if(this.value === v){
33914             return;
33915         }
33916         
33917         this.value = v;
33918         
33919         if(this.rendered){
33920             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33921         }
33922         
33923         Roo.each(this.radioes, function(i){
33924             i.checked = false;
33925             i.el.removeClass('checked');
33926         });
33927         
33928         Roo.each(this.radioes, function(i){
33929             
33930             if(i.value === v || i.value.toString() === v.toString()){
33931                 i.checked = true;
33932                 i.el.addClass('checked');
33933                 
33934                 if(suppressEvent !== true){
33935                     this.fireEvent('check', this, i);
33936                 }
33937                 
33938                 return false;
33939             }
33940             
33941         }, this);
33942         
33943         this.validate();
33944     },
33945     
33946     clearInvalid : function(){
33947         
33948         if(!this.el || this.preventMark){
33949             return;
33950         }
33951         
33952         this.el.removeClass([this.invalidClass]);
33953         
33954         this.fireEvent('valid', this);
33955     }
33956     
33957 });
33958
33959 Roo.apply(Roo.bootstrap.RadioSet, {
33960     
33961     groups: {},
33962     
33963     register : function(set)
33964     {
33965         this.groups[set.name] = set;
33966     },
33967     
33968     get: function(name) 
33969     {
33970         if (typeof(this.groups[name]) == 'undefined') {
33971             return false;
33972         }
33973         
33974         return this.groups[name] ;
33975     }
33976     
33977 });
33978 /*
33979  * Based on:
33980  * Ext JS Library 1.1.1
33981  * Copyright(c) 2006-2007, Ext JS, LLC.
33982  *
33983  * Originally Released Under LGPL - original licence link has changed is not relivant.
33984  *
33985  * Fork - LGPL
33986  * <script type="text/javascript">
33987  */
33988
33989
33990 /**
33991  * @class Roo.bootstrap.SplitBar
33992  * @extends Roo.util.Observable
33993  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33994  * <br><br>
33995  * Usage:
33996  * <pre><code>
33997 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33998                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33999 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34000 split.minSize = 100;
34001 split.maxSize = 600;
34002 split.animate = true;
34003 split.on('moved', splitterMoved);
34004 </code></pre>
34005  * @constructor
34006  * Create a new SplitBar
34007  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34008  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34009  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34010  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34011                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34012                         position of the SplitBar).
34013  */
34014 Roo.bootstrap.SplitBar = function(cfg){
34015     
34016     /** @private */
34017     
34018     //{
34019     //  dragElement : elm
34020     //  resizingElement: el,
34021         // optional..
34022     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34023     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34024         // existingProxy ???
34025     //}
34026     
34027     this.el = Roo.get(cfg.dragElement, true);
34028     this.el.dom.unselectable = "on";
34029     /** @private */
34030     this.resizingEl = Roo.get(cfg.resizingElement, true);
34031
34032     /**
34033      * @private
34034      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34035      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34036      * @type Number
34037      */
34038     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34039     
34040     /**
34041      * The minimum size of the resizing element. (Defaults to 0)
34042      * @type Number
34043      */
34044     this.minSize = 0;
34045     
34046     /**
34047      * The maximum size of the resizing element. (Defaults to 2000)
34048      * @type Number
34049      */
34050     this.maxSize = 2000;
34051     
34052     /**
34053      * Whether to animate the transition to the new size
34054      * @type Boolean
34055      */
34056     this.animate = false;
34057     
34058     /**
34059      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34060      * @type Boolean
34061      */
34062     this.useShim = false;
34063     
34064     /** @private */
34065     this.shim = null;
34066     
34067     if(!cfg.existingProxy){
34068         /** @private */
34069         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34070     }else{
34071         this.proxy = Roo.get(cfg.existingProxy).dom;
34072     }
34073     /** @private */
34074     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34075     
34076     /** @private */
34077     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34078     
34079     /** @private */
34080     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34081     
34082     /** @private */
34083     this.dragSpecs = {};
34084     
34085     /**
34086      * @private The adapter to use to positon and resize elements
34087      */
34088     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34089     this.adapter.init(this);
34090     
34091     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34092         /** @private */
34093         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34094         this.el.addClass("roo-splitbar-h");
34095     }else{
34096         /** @private */
34097         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34098         this.el.addClass("roo-splitbar-v");
34099     }
34100     
34101     this.addEvents({
34102         /**
34103          * @event resize
34104          * Fires when the splitter is moved (alias for {@link #event-moved})
34105          * @param {Roo.bootstrap.SplitBar} this
34106          * @param {Number} newSize the new width or height
34107          */
34108         "resize" : true,
34109         /**
34110          * @event moved
34111          * Fires when the splitter is moved
34112          * @param {Roo.bootstrap.SplitBar} this
34113          * @param {Number} newSize the new width or height
34114          */
34115         "moved" : true,
34116         /**
34117          * @event beforeresize
34118          * Fires before the splitter is dragged
34119          * @param {Roo.bootstrap.SplitBar} this
34120          */
34121         "beforeresize" : true,
34122
34123         "beforeapply" : true
34124     });
34125
34126     Roo.util.Observable.call(this);
34127 };
34128
34129 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34130     onStartProxyDrag : function(x, y){
34131         this.fireEvent("beforeresize", this);
34132         if(!this.overlay){
34133             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34134             o.unselectable();
34135             o.enableDisplayMode("block");
34136             // all splitbars share the same overlay
34137             Roo.bootstrap.SplitBar.prototype.overlay = o;
34138         }
34139         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34140         this.overlay.show();
34141         Roo.get(this.proxy).setDisplayed("block");
34142         var size = this.adapter.getElementSize(this);
34143         this.activeMinSize = this.getMinimumSize();;
34144         this.activeMaxSize = this.getMaximumSize();;
34145         var c1 = size - this.activeMinSize;
34146         var c2 = Math.max(this.activeMaxSize - size, 0);
34147         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34148             this.dd.resetConstraints();
34149             this.dd.setXConstraint(
34150                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34151                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34152             );
34153             this.dd.setYConstraint(0, 0);
34154         }else{
34155             this.dd.resetConstraints();
34156             this.dd.setXConstraint(0, 0);
34157             this.dd.setYConstraint(
34158                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34159                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34160             );
34161          }
34162         this.dragSpecs.startSize = size;
34163         this.dragSpecs.startPoint = [x, y];
34164         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34165     },
34166     
34167     /** 
34168      * @private Called after the drag operation by the DDProxy
34169      */
34170     onEndProxyDrag : function(e){
34171         Roo.get(this.proxy).setDisplayed(false);
34172         var endPoint = Roo.lib.Event.getXY(e);
34173         if(this.overlay){
34174             this.overlay.hide();
34175         }
34176         var newSize;
34177         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34178             newSize = this.dragSpecs.startSize + 
34179                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34180                     endPoint[0] - this.dragSpecs.startPoint[0] :
34181                     this.dragSpecs.startPoint[0] - endPoint[0]
34182                 );
34183         }else{
34184             newSize = this.dragSpecs.startSize + 
34185                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34186                     endPoint[1] - this.dragSpecs.startPoint[1] :
34187                     this.dragSpecs.startPoint[1] - endPoint[1]
34188                 );
34189         }
34190         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34191         if(newSize != this.dragSpecs.startSize){
34192             if(this.fireEvent('beforeapply', this, newSize) !== false){
34193                 this.adapter.setElementSize(this, newSize);
34194                 this.fireEvent("moved", this, newSize);
34195                 this.fireEvent("resize", this, newSize);
34196             }
34197         }
34198     },
34199     
34200     /**
34201      * Get the adapter this SplitBar uses
34202      * @return The adapter object
34203      */
34204     getAdapter : function(){
34205         return this.adapter;
34206     },
34207     
34208     /**
34209      * Set the adapter this SplitBar uses
34210      * @param {Object} adapter A SplitBar adapter object
34211      */
34212     setAdapter : function(adapter){
34213         this.adapter = adapter;
34214         this.adapter.init(this);
34215     },
34216     
34217     /**
34218      * Gets the minimum size for the resizing element
34219      * @return {Number} The minimum size
34220      */
34221     getMinimumSize : function(){
34222         return this.minSize;
34223     },
34224     
34225     /**
34226      * Sets the minimum size for the resizing element
34227      * @param {Number} minSize The minimum size
34228      */
34229     setMinimumSize : function(minSize){
34230         this.minSize = minSize;
34231     },
34232     
34233     /**
34234      * Gets the maximum size for the resizing element
34235      * @return {Number} The maximum size
34236      */
34237     getMaximumSize : function(){
34238         return this.maxSize;
34239     },
34240     
34241     /**
34242      * Sets the maximum size for the resizing element
34243      * @param {Number} maxSize The maximum size
34244      */
34245     setMaximumSize : function(maxSize){
34246         this.maxSize = maxSize;
34247     },
34248     
34249     /**
34250      * Sets the initialize size for the resizing element
34251      * @param {Number} size The initial size
34252      */
34253     setCurrentSize : function(size){
34254         var oldAnimate = this.animate;
34255         this.animate = false;
34256         this.adapter.setElementSize(this, size);
34257         this.animate = oldAnimate;
34258     },
34259     
34260     /**
34261      * Destroy this splitbar. 
34262      * @param {Boolean} removeEl True to remove the element
34263      */
34264     destroy : function(removeEl){
34265         if(this.shim){
34266             this.shim.remove();
34267         }
34268         this.dd.unreg();
34269         this.proxy.parentNode.removeChild(this.proxy);
34270         if(removeEl){
34271             this.el.remove();
34272         }
34273     }
34274 });
34275
34276 /**
34277  * @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.
34278  */
34279 Roo.bootstrap.SplitBar.createProxy = function(dir){
34280     var proxy = new Roo.Element(document.createElement("div"));
34281     proxy.unselectable();
34282     var cls = 'roo-splitbar-proxy';
34283     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34284     document.body.appendChild(proxy.dom);
34285     return proxy.dom;
34286 };
34287
34288 /** 
34289  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34290  * Default Adapter. It assumes the splitter and resizing element are not positioned
34291  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34292  */
34293 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34294 };
34295
34296 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34297     // do nothing for now
34298     init : function(s){
34299     
34300     },
34301     /**
34302      * Called before drag operations to get the current size of the resizing element. 
34303      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34304      */
34305      getElementSize : function(s){
34306         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34307             return s.resizingEl.getWidth();
34308         }else{
34309             return s.resizingEl.getHeight();
34310         }
34311     },
34312     
34313     /**
34314      * Called after drag operations to set the size of the resizing element.
34315      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34316      * @param {Number} newSize The new size to set
34317      * @param {Function} onComplete A function to be invoked when resizing is complete
34318      */
34319     setElementSize : function(s, newSize, onComplete){
34320         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34321             if(!s.animate){
34322                 s.resizingEl.setWidth(newSize);
34323                 if(onComplete){
34324                     onComplete(s, newSize);
34325                 }
34326             }else{
34327                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34328             }
34329         }else{
34330             
34331             if(!s.animate){
34332                 s.resizingEl.setHeight(newSize);
34333                 if(onComplete){
34334                     onComplete(s, newSize);
34335                 }
34336             }else{
34337                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34338             }
34339         }
34340     }
34341 };
34342
34343 /** 
34344  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34345  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34346  * Adapter that  moves the splitter element to align with the resized sizing element. 
34347  * Used with an absolute positioned SplitBar.
34348  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34349  * document.body, make sure you assign an id to the body element.
34350  */
34351 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34352     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34353     this.container = Roo.get(container);
34354 };
34355
34356 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34357     init : function(s){
34358         this.basic.init(s);
34359     },
34360     
34361     getElementSize : function(s){
34362         return this.basic.getElementSize(s);
34363     },
34364     
34365     setElementSize : function(s, newSize, onComplete){
34366         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34367     },
34368     
34369     moveSplitter : function(s){
34370         var yes = Roo.bootstrap.SplitBar;
34371         switch(s.placement){
34372             case yes.LEFT:
34373                 s.el.setX(s.resizingEl.getRight());
34374                 break;
34375             case yes.RIGHT:
34376                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34377                 break;
34378             case yes.TOP:
34379                 s.el.setY(s.resizingEl.getBottom());
34380                 break;
34381             case yes.BOTTOM:
34382                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34383                 break;
34384         }
34385     }
34386 };
34387
34388 /**
34389  * Orientation constant - Create a vertical SplitBar
34390  * @static
34391  * @type Number
34392  */
34393 Roo.bootstrap.SplitBar.VERTICAL = 1;
34394
34395 /**
34396  * Orientation constant - Create a horizontal SplitBar
34397  * @static
34398  * @type Number
34399  */
34400 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34401
34402 /**
34403  * Placement constant - The resizing element is to the left of the splitter element
34404  * @static
34405  * @type Number
34406  */
34407 Roo.bootstrap.SplitBar.LEFT = 1;
34408
34409 /**
34410  * Placement constant - The resizing element is to the right of the splitter element
34411  * @static
34412  * @type Number
34413  */
34414 Roo.bootstrap.SplitBar.RIGHT = 2;
34415
34416 /**
34417  * Placement constant - The resizing element is positioned above the splitter element
34418  * @static
34419  * @type Number
34420  */
34421 Roo.bootstrap.SplitBar.TOP = 3;
34422
34423 /**
34424  * Placement constant - The resizing element is positioned under splitter element
34425  * @static
34426  * @type Number
34427  */
34428 Roo.bootstrap.SplitBar.BOTTOM = 4;
34429 Roo.namespace("Roo.bootstrap.layout");/*
34430  * Based on:
34431  * Ext JS Library 1.1.1
34432  * Copyright(c) 2006-2007, Ext JS, LLC.
34433  *
34434  * Originally Released Under LGPL - original licence link has changed is not relivant.
34435  *
34436  * Fork - LGPL
34437  * <script type="text/javascript">
34438  */
34439
34440 /**
34441  * @class Roo.bootstrap.layout.Manager
34442  * @extends Roo.bootstrap.Component
34443  * Base class for layout managers.
34444  */
34445 Roo.bootstrap.layout.Manager = function(config)
34446 {
34447     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34448
34449
34450
34451
34452
34453     /** false to disable window resize monitoring @type Boolean */
34454     this.monitorWindowResize = true;
34455     this.regions = {};
34456     this.addEvents({
34457         /**
34458          * @event layout
34459          * Fires when a layout is performed.
34460          * @param {Roo.LayoutManager} this
34461          */
34462         "layout" : true,
34463         /**
34464          * @event regionresized
34465          * Fires when the user resizes a region.
34466          * @param {Roo.LayoutRegion} region The resized region
34467          * @param {Number} newSize The new size (width for east/west, height for north/south)
34468          */
34469         "regionresized" : true,
34470         /**
34471          * @event regioncollapsed
34472          * Fires when a region is collapsed.
34473          * @param {Roo.LayoutRegion} region The collapsed region
34474          */
34475         "regioncollapsed" : true,
34476         /**
34477          * @event regionexpanded
34478          * Fires when a region is expanded.
34479          * @param {Roo.LayoutRegion} region The expanded region
34480          */
34481         "regionexpanded" : true
34482     });
34483     this.updating = false;
34484
34485     if (config.el) {
34486         this.el = Roo.get(config.el);
34487         this.initEvents();
34488     }
34489
34490 };
34491
34492 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34493
34494
34495     regions : null,
34496
34497     monitorWindowResize : true,
34498
34499
34500     updating : false,
34501
34502
34503     onRender : function(ct, position)
34504     {
34505         if(!this.el){
34506             this.el = Roo.get(ct);
34507             this.initEvents();
34508         }
34509         //this.fireEvent('render',this);
34510     },
34511
34512
34513     initEvents: function()
34514     {
34515
34516
34517         // ie scrollbar fix
34518         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34519             document.body.scroll = "no";
34520         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34521             this.el.position('relative');
34522         }
34523         this.id = this.el.id;
34524         this.el.addClass("roo-layout-container");
34525         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34526         if(this.el.dom != document.body ) {
34527             this.el.on('resize', this.layout,this);
34528             this.el.on('show', this.layout,this);
34529         }
34530
34531     },
34532
34533     /**
34534      * Returns true if this layout is currently being updated
34535      * @return {Boolean}
34536      */
34537     isUpdating : function(){
34538         return this.updating;
34539     },
34540
34541     /**
34542      * Suspend the LayoutManager from doing auto-layouts while
34543      * making multiple add or remove calls
34544      */
34545     beginUpdate : function(){
34546         this.updating = true;
34547     },
34548
34549     /**
34550      * Restore auto-layouts and optionally disable the manager from performing a layout
34551      * @param {Boolean} noLayout true to disable a layout update
34552      */
34553     endUpdate : function(noLayout){
34554         this.updating = false;
34555         if(!noLayout){
34556             this.layout();
34557         }
34558     },
34559
34560     layout: function(){
34561         // abstract...
34562     },
34563
34564     onRegionResized : function(region, newSize){
34565         this.fireEvent("regionresized", region, newSize);
34566         this.layout();
34567     },
34568
34569     onRegionCollapsed : function(region){
34570         this.fireEvent("regioncollapsed", region);
34571     },
34572
34573     onRegionExpanded : function(region){
34574         this.fireEvent("regionexpanded", region);
34575     },
34576
34577     /**
34578      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34579      * performs box-model adjustments.
34580      * @return {Object} The size as an object {width: (the width), height: (the height)}
34581      */
34582     getViewSize : function()
34583     {
34584         var size;
34585         if(this.el.dom != document.body){
34586             size = this.el.getSize();
34587         }else{
34588             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34589         }
34590         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34591         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34592         return size;
34593     },
34594
34595     /**
34596      * Returns the Element this layout is bound to.
34597      * @return {Roo.Element}
34598      */
34599     getEl : function(){
34600         return this.el;
34601     },
34602
34603     /**
34604      * Returns the specified region.
34605      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34606      * @return {Roo.LayoutRegion}
34607      */
34608     getRegion : function(target){
34609         return this.regions[target.toLowerCase()];
34610     },
34611
34612     onWindowResize : function(){
34613         if(this.monitorWindowResize){
34614             this.layout();
34615         }
34616     }
34617 });
34618 /*
34619  * Based on:
34620  * Ext JS Library 1.1.1
34621  * Copyright(c) 2006-2007, Ext JS, LLC.
34622  *
34623  * Originally Released Under LGPL - original licence link has changed is not relivant.
34624  *
34625  * Fork - LGPL
34626  * <script type="text/javascript">
34627  */
34628 /**
34629  * @class Roo.bootstrap.layout.Border
34630  * @extends Roo.bootstrap.layout.Manager
34631  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34632  * please see: examples/bootstrap/nested.html<br><br>
34633  
34634 <b>The container the layout is rendered into can be either the body element or any other element.
34635 If it is not the body element, the container needs to either be an absolute positioned element,
34636 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34637 the container size if it is not the body element.</b>
34638
34639 * @constructor
34640 * Create a new Border
34641 * @param {Object} config Configuration options
34642  */
34643 Roo.bootstrap.layout.Border = function(config){
34644     config = config || {};
34645     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34646     
34647     
34648     
34649     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34650         if(config[region]){
34651             config[region].region = region;
34652             this.addRegion(config[region]);
34653         }
34654     },this);
34655     
34656 };
34657
34658 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34659
34660 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34661     /**
34662      * Creates and adds a new region if it doesn't already exist.
34663      * @param {String} target The target region key (north, south, east, west or center).
34664      * @param {Object} config The regions config object
34665      * @return {BorderLayoutRegion} The new region
34666      */
34667     addRegion : function(config)
34668     {
34669         if(!this.regions[config.region]){
34670             var r = this.factory(config);
34671             this.bindRegion(r);
34672         }
34673         return this.regions[config.region];
34674     },
34675
34676     // private (kinda)
34677     bindRegion : function(r){
34678         this.regions[r.config.region] = r;
34679         
34680         r.on("visibilitychange",    this.layout, this);
34681         r.on("paneladded",          this.layout, this);
34682         r.on("panelremoved",        this.layout, this);
34683         r.on("invalidated",         this.layout, this);
34684         r.on("resized",             this.onRegionResized, this);
34685         r.on("collapsed",           this.onRegionCollapsed, this);
34686         r.on("expanded",            this.onRegionExpanded, this);
34687     },
34688
34689     /**
34690      * Performs a layout update.
34691      */
34692     layout : function()
34693     {
34694         if(this.updating) {
34695             return;
34696         }
34697         
34698         // render all the rebions if they have not been done alreayd?
34699         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34700             if(this.regions[region] && !this.regions[region].bodyEl){
34701                 this.regions[region].onRender(this.el)
34702             }
34703         },this);
34704         
34705         var size = this.getViewSize();
34706         var w = size.width;
34707         var h = size.height;
34708         var centerW = w;
34709         var centerH = h;
34710         var centerY = 0;
34711         var centerX = 0;
34712         //var x = 0, y = 0;
34713
34714         var rs = this.regions;
34715         var north = rs["north"];
34716         var south = rs["south"]; 
34717         var west = rs["west"];
34718         var east = rs["east"];
34719         var center = rs["center"];
34720         //if(this.hideOnLayout){ // not supported anymore
34721             //c.el.setStyle("display", "none");
34722         //}
34723         if(north && north.isVisible()){
34724             var b = north.getBox();
34725             var m = north.getMargins();
34726             b.width = w - (m.left+m.right);
34727             b.x = m.left;
34728             b.y = m.top;
34729             centerY = b.height + b.y + m.bottom;
34730             centerH -= centerY;
34731             north.updateBox(this.safeBox(b));
34732         }
34733         if(south && south.isVisible()){
34734             var b = south.getBox();
34735             var m = south.getMargins();
34736             b.width = w - (m.left+m.right);
34737             b.x = m.left;
34738             var totalHeight = (b.height + m.top + m.bottom);
34739             b.y = h - totalHeight + m.top;
34740             centerH -= totalHeight;
34741             south.updateBox(this.safeBox(b));
34742         }
34743         if(west && west.isVisible()){
34744             var b = west.getBox();
34745             var m = west.getMargins();
34746             b.height = centerH - (m.top+m.bottom);
34747             b.x = m.left;
34748             b.y = centerY + m.top;
34749             var totalWidth = (b.width + m.left + m.right);
34750             centerX += totalWidth;
34751             centerW -= totalWidth;
34752             west.updateBox(this.safeBox(b));
34753         }
34754         if(east && east.isVisible()){
34755             var b = east.getBox();
34756             var m = east.getMargins();
34757             b.height = centerH - (m.top+m.bottom);
34758             var totalWidth = (b.width + m.left + m.right);
34759             b.x = w - totalWidth + m.left;
34760             b.y = centerY + m.top;
34761             centerW -= totalWidth;
34762             east.updateBox(this.safeBox(b));
34763         }
34764         if(center){
34765             var m = center.getMargins();
34766             var centerBox = {
34767                 x: centerX + m.left,
34768                 y: centerY + m.top,
34769                 width: centerW - (m.left+m.right),
34770                 height: centerH - (m.top+m.bottom)
34771             };
34772             //if(this.hideOnLayout){
34773                 //center.el.setStyle("display", "block");
34774             //}
34775             center.updateBox(this.safeBox(centerBox));
34776         }
34777         this.el.repaint();
34778         this.fireEvent("layout", this);
34779     },
34780
34781     // private
34782     safeBox : function(box){
34783         box.width = Math.max(0, box.width);
34784         box.height = Math.max(0, box.height);
34785         return box;
34786     },
34787
34788     /**
34789      * Adds a ContentPanel (or subclass) to this layout.
34790      * @param {String} target The target region key (north, south, east, west or center).
34791      * @param {Roo.ContentPanel} panel The panel to add
34792      * @return {Roo.ContentPanel} The added panel
34793      */
34794     add : function(target, panel){
34795          
34796         target = target.toLowerCase();
34797         return this.regions[target].add(panel);
34798     },
34799
34800     /**
34801      * Remove a ContentPanel (or subclass) to this layout.
34802      * @param {String} target The target region key (north, south, east, west or center).
34803      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34804      * @return {Roo.ContentPanel} The removed panel
34805      */
34806     remove : function(target, panel){
34807         target = target.toLowerCase();
34808         return this.regions[target].remove(panel);
34809     },
34810
34811     /**
34812      * Searches all regions for a panel with the specified id
34813      * @param {String} panelId
34814      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34815      */
34816     findPanel : function(panelId){
34817         var rs = this.regions;
34818         for(var target in rs){
34819             if(typeof rs[target] != "function"){
34820                 var p = rs[target].getPanel(panelId);
34821                 if(p){
34822                     return p;
34823                 }
34824             }
34825         }
34826         return null;
34827     },
34828
34829     /**
34830      * Searches all regions for a panel with the specified id and activates (shows) it.
34831      * @param {String/ContentPanel} panelId The panels id or the panel itself
34832      * @return {Roo.ContentPanel} The shown panel or null
34833      */
34834     showPanel : function(panelId) {
34835       var rs = this.regions;
34836       for(var target in rs){
34837          var r = rs[target];
34838          if(typeof r != "function"){
34839             if(r.hasPanel(panelId)){
34840                return r.showPanel(panelId);
34841             }
34842          }
34843       }
34844       return null;
34845    },
34846
34847    /**
34848      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34849      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34850      */
34851    /*
34852     restoreState : function(provider){
34853         if(!provider){
34854             provider = Roo.state.Manager;
34855         }
34856         var sm = new Roo.LayoutStateManager();
34857         sm.init(this, provider);
34858     },
34859 */
34860  
34861  
34862     /**
34863      * Adds a xtype elements to the layout.
34864      * <pre><code>
34865
34866 layout.addxtype({
34867        xtype : 'ContentPanel',
34868        region: 'west',
34869        items: [ .... ]
34870    }
34871 );
34872
34873 layout.addxtype({
34874         xtype : 'NestedLayoutPanel',
34875         region: 'west',
34876         layout: {
34877            center: { },
34878            west: { }   
34879         },
34880         items : [ ... list of content panels or nested layout panels.. ]
34881    }
34882 );
34883 </code></pre>
34884      * @param {Object} cfg Xtype definition of item to add.
34885      */
34886     addxtype : function(cfg)
34887     {
34888         // basically accepts a pannel...
34889         // can accept a layout region..!?!?
34890         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34891         
34892         
34893         // theory?  children can only be panels??
34894         
34895         //if (!cfg.xtype.match(/Panel$/)) {
34896         //    return false;
34897         //}
34898         var ret = false;
34899         
34900         if (typeof(cfg.region) == 'undefined') {
34901             Roo.log("Failed to add Panel, region was not set");
34902             Roo.log(cfg);
34903             return false;
34904         }
34905         var region = cfg.region;
34906         delete cfg.region;
34907         
34908           
34909         var xitems = [];
34910         if (cfg.items) {
34911             xitems = cfg.items;
34912             delete cfg.items;
34913         }
34914         var nb = false;
34915         
34916         switch(cfg.xtype) 
34917         {
34918             case 'Content':  // ContentPanel (el, cfg)
34919             case 'Scroll':  // ContentPanel (el, cfg)
34920             case 'View': 
34921                 cfg.autoCreate = true;
34922                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34923                 //} else {
34924                 //    var el = this.el.createChild();
34925                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34926                 //}
34927                 
34928                 this.add(region, ret);
34929                 break;
34930             
34931             /*
34932             case 'TreePanel': // our new panel!
34933                 cfg.el = this.el.createChild();
34934                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34935                 this.add(region, ret);
34936                 break;
34937             */
34938             
34939             case 'Nest': 
34940                 // create a new Layout (which is  a Border Layout...
34941                 
34942                 var clayout = cfg.layout;
34943                 clayout.el  = this.el.createChild();
34944                 clayout.items   = clayout.items  || [];
34945                 
34946                 delete cfg.layout;
34947                 
34948                 // replace this exitems with the clayout ones..
34949                 xitems = clayout.items;
34950                  
34951                 // force background off if it's in center...
34952                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34953                     cfg.background = false;
34954                 }
34955                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34956                 
34957                 
34958                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34959                 //console.log('adding nested layout panel '  + cfg.toSource());
34960                 this.add(region, ret);
34961                 nb = {}; /// find first...
34962                 break;
34963             
34964             case 'Grid':
34965                 
34966                 // needs grid and region
34967                 
34968                 //var el = this.getRegion(region).el.createChild();
34969                 /*
34970                  *var el = this.el.createChild();
34971                 // create the grid first...
34972                 cfg.grid.container = el;
34973                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34974                 */
34975                 
34976                 if (region == 'center' && this.active ) {
34977                     cfg.background = false;
34978                 }
34979                 
34980                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34981                 
34982                 this.add(region, ret);
34983                 /*
34984                 if (cfg.background) {
34985                     // render grid on panel activation (if panel background)
34986                     ret.on('activate', function(gp) {
34987                         if (!gp.grid.rendered) {
34988                     //        gp.grid.render(el);
34989                         }
34990                     });
34991                 } else {
34992                   //  cfg.grid.render(el);
34993                 }
34994                 */
34995                 break;
34996            
34997            
34998             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34999                 // it was the old xcomponent building that caused this before.
35000                 // espeically if border is the top element in the tree.
35001                 ret = this;
35002                 break; 
35003                 
35004                     
35005                 
35006                 
35007                 
35008             default:
35009                 /*
35010                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35011                     
35012                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35013                     this.add(region, ret);
35014                 } else {
35015                 */
35016                     Roo.log(cfg);
35017                     throw "Can not add '" + cfg.xtype + "' to Border";
35018                     return null;
35019              
35020                                 
35021              
35022         }
35023         this.beginUpdate();
35024         // add children..
35025         var region = '';
35026         var abn = {};
35027         Roo.each(xitems, function(i)  {
35028             region = nb && i.region ? i.region : false;
35029             
35030             var add = ret.addxtype(i);
35031            
35032             if (region) {
35033                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35034                 if (!i.background) {
35035                     abn[region] = nb[region] ;
35036                 }
35037             }
35038             
35039         });
35040         this.endUpdate();
35041
35042         // make the last non-background panel active..
35043         //if (nb) { Roo.log(abn); }
35044         if (nb) {
35045             
35046             for(var r in abn) {
35047                 region = this.getRegion(r);
35048                 if (region) {
35049                     // tried using nb[r], but it does not work..
35050                      
35051                     region.showPanel(abn[r]);
35052                    
35053                 }
35054             }
35055         }
35056         return ret;
35057         
35058     },
35059     
35060     
35061 // private
35062     factory : function(cfg)
35063     {
35064         
35065         var validRegions = Roo.bootstrap.layout.Border.regions;
35066
35067         var target = cfg.region;
35068         cfg.mgr = this;
35069         
35070         var r = Roo.bootstrap.layout;
35071         Roo.log(target);
35072         switch(target){
35073             case "north":
35074                 return new r.North(cfg);
35075             case "south":
35076                 return new r.South(cfg);
35077             case "east":
35078                 return new r.East(cfg);
35079             case "west":
35080                 return new r.West(cfg);
35081             case "center":
35082                 return new r.Center(cfg);
35083         }
35084         throw 'Layout region "'+target+'" not supported.';
35085     }
35086     
35087     
35088 });
35089  /*
35090  * Based on:
35091  * Ext JS Library 1.1.1
35092  * Copyright(c) 2006-2007, Ext JS, LLC.
35093  *
35094  * Originally Released Under LGPL - original licence link has changed is not relivant.
35095  *
35096  * Fork - LGPL
35097  * <script type="text/javascript">
35098  */
35099  
35100 /**
35101  * @class Roo.bootstrap.layout.Basic
35102  * @extends Roo.util.Observable
35103  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35104  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35105  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35106  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35107  * @cfg {string}   region  the region that it inhabits..
35108  * @cfg {bool}   skipConfig skip config?
35109  * 
35110
35111  */
35112 Roo.bootstrap.layout.Basic = function(config){
35113     
35114     this.mgr = config.mgr;
35115     
35116     this.position = config.region;
35117     
35118     var skipConfig = config.skipConfig;
35119     
35120     this.events = {
35121         /**
35122          * @scope Roo.BasicLayoutRegion
35123          */
35124         
35125         /**
35126          * @event beforeremove
35127          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35128          * @param {Roo.LayoutRegion} this
35129          * @param {Roo.ContentPanel} panel The panel
35130          * @param {Object} e The cancel event object
35131          */
35132         "beforeremove" : true,
35133         /**
35134          * @event invalidated
35135          * Fires when the layout for this region is changed.
35136          * @param {Roo.LayoutRegion} this
35137          */
35138         "invalidated" : true,
35139         /**
35140          * @event visibilitychange
35141          * Fires when this region is shown or hidden 
35142          * @param {Roo.LayoutRegion} this
35143          * @param {Boolean} visibility true or false
35144          */
35145         "visibilitychange" : true,
35146         /**
35147          * @event paneladded
35148          * Fires when a panel is added. 
35149          * @param {Roo.LayoutRegion} this
35150          * @param {Roo.ContentPanel} panel The panel
35151          */
35152         "paneladded" : true,
35153         /**
35154          * @event panelremoved
35155          * Fires when a panel is removed. 
35156          * @param {Roo.LayoutRegion} this
35157          * @param {Roo.ContentPanel} panel The panel
35158          */
35159         "panelremoved" : true,
35160         /**
35161          * @event beforecollapse
35162          * Fires when this region before collapse.
35163          * @param {Roo.LayoutRegion} this
35164          */
35165         "beforecollapse" : true,
35166         /**
35167          * @event collapsed
35168          * Fires when this region is collapsed.
35169          * @param {Roo.LayoutRegion} this
35170          */
35171         "collapsed" : true,
35172         /**
35173          * @event expanded
35174          * Fires when this region is expanded.
35175          * @param {Roo.LayoutRegion} this
35176          */
35177         "expanded" : true,
35178         /**
35179          * @event slideshow
35180          * Fires when this region is slid into view.
35181          * @param {Roo.LayoutRegion} this
35182          */
35183         "slideshow" : true,
35184         /**
35185          * @event slidehide
35186          * Fires when this region slides out of view. 
35187          * @param {Roo.LayoutRegion} this
35188          */
35189         "slidehide" : true,
35190         /**
35191          * @event panelactivated
35192          * Fires when a panel is activated. 
35193          * @param {Roo.LayoutRegion} this
35194          * @param {Roo.ContentPanel} panel The activated panel
35195          */
35196         "panelactivated" : true,
35197         /**
35198          * @event resized
35199          * Fires when the user resizes this region. 
35200          * @param {Roo.LayoutRegion} this
35201          * @param {Number} newSize The new size (width for east/west, height for north/south)
35202          */
35203         "resized" : true
35204     };
35205     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35206     this.panels = new Roo.util.MixedCollection();
35207     this.panels.getKey = this.getPanelId.createDelegate(this);
35208     this.box = null;
35209     this.activePanel = null;
35210     // ensure listeners are added...
35211     
35212     if (config.listeners || config.events) {
35213         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35214             listeners : config.listeners || {},
35215             events : config.events || {}
35216         });
35217     }
35218     
35219     if(skipConfig !== true){
35220         this.applyConfig(config);
35221     }
35222 };
35223
35224 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35225 {
35226     getPanelId : function(p){
35227         return p.getId();
35228     },
35229     
35230     applyConfig : function(config){
35231         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35232         this.config = config;
35233         
35234     },
35235     
35236     /**
35237      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35238      * the width, for horizontal (north, south) the height.
35239      * @param {Number} newSize The new width or height
35240      */
35241     resizeTo : function(newSize){
35242         var el = this.el ? this.el :
35243                  (this.activePanel ? this.activePanel.getEl() : null);
35244         if(el){
35245             switch(this.position){
35246                 case "east":
35247                 case "west":
35248                     el.setWidth(newSize);
35249                     this.fireEvent("resized", this, newSize);
35250                 break;
35251                 case "north":
35252                 case "south":
35253                     el.setHeight(newSize);
35254                     this.fireEvent("resized", this, newSize);
35255                 break;                
35256             }
35257         }
35258     },
35259     
35260     getBox : function(){
35261         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35262     },
35263     
35264     getMargins : function(){
35265         return this.margins;
35266     },
35267     
35268     updateBox : function(box){
35269         this.box = box;
35270         var el = this.activePanel.getEl();
35271         el.dom.style.left = box.x + "px";
35272         el.dom.style.top = box.y + "px";
35273         this.activePanel.setSize(box.width, box.height);
35274     },
35275     
35276     /**
35277      * Returns the container element for this region.
35278      * @return {Roo.Element}
35279      */
35280     getEl : function(){
35281         return this.activePanel;
35282     },
35283     
35284     /**
35285      * Returns true if this region is currently visible.
35286      * @return {Boolean}
35287      */
35288     isVisible : function(){
35289         return this.activePanel ? true : false;
35290     },
35291     
35292     setActivePanel : function(panel){
35293         panel = this.getPanel(panel);
35294         if(this.activePanel && this.activePanel != panel){
35295             this.activePanel.setActiveState(false);
35296             this.activePanel.getEl().setLeftTop(-10000,-10000);
35297         }
35298         this.activePanel = panel;
35299         panel.setActiveState(true);
35300         if(this.box){
35301             panel.setSize(this.box.width, this.box.height);
35302         }
35303         this.fireEvent("panelactivated", this, panel);
35304         this.fireEvent("invalidated");
35305     },
35306     
35307     /**
35308      * Show the specified panel.
35309      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35310      * @return {Roo.ContentPanel} The shown panel or null
35311      */
35312     showPanel : function(panel){
35313         panel = this.getPanel(panel);
35314         if(panel){
35315             this.setActivePanel(panel);
35316         }
35317         return panel;
35318     },
35319     
35320     /**
35321      * Get the active panel for this region.
35322      * @return {Roo.ContentPanel} The active panel or null
35323      */
35324     getActivePanel : function(){
35325         return this.activePanel;
35326     },
35327     
35328     /**
35329      * Add the passed ContentPanel(s)
35330      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35331      * @return {Roo.ContentPanel} The panel added (if only one was added)
35332      */
35333     add : function(panel){
35334         if(arguments.length > 1){
35335             for(var i = 0, len = arguments.length; i < len; i++) {
35336                 this.add(arguments[i]);
35337             }
35338             return null;
35339         }
35340         if(this.hasPanel(panel)){
35341             this.showPanel(panel);
35342             return panel;
35343         }
35344         var el = panel.getEl();
35345         if(el.dom.parentNode != this.mgr.el.dom){
35346             this.mgr.el.dom.appendChild(el.dom);
35347         }
35348         if(panel.setRegion){
35349             panel.setRegion(this);
35350         }
35351         this.panels.add(panel);
35352         el.setStyle("position", "absolute");
35353         if(!panel.background){
35354             this.setActivePanel(panel);
35355             if(this.config.initialSize && this.panels.getCount()==1){
35356                 this.resizeTo(this.config.initialSize);
35357             }
35358         }
35359         this.fireEvent("paneladded", this, panel);
35360         return panel;
35361     },
35362     
35363     /**
35364      * Returns true if the panel is in this region.
35365      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35366      * @return {Boolean}
35367      */
35368     hasPanel : function(panel){
35369         if(typeof panel == "object"){ // must be panel obj
35370             panel = panel.getId();
35371         }
35372         return this.getPanel(panel) ? true : false;
35373     },
35374     
35375     /**
35376      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35377      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35378      * @param {Boolean} preservePanel Overrides the config preservePanel option
35379      * @return {Roo.ContentPanel} The panel that was removed
35380      */
35381     remove : function(panel, preservePanel){
35382         panel = this.getPanel(panel);
35383         if(!panel){
35384             return null;
35385         }
35386         var e = {};
35387         this.fireEvent("beforeremove", this, panel, e);
35388         if(e.cancel === true){
35389             return null;
35390         }
35391         var panelId = panel.getId();
35392         this.panels.removeKey(panelId);
35393         return panel;
35394     },
35395     
35396     /**
35397      * Returns the panel specified or null if it's not in this region.
35398      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35399      * @return {Roo.ContentPanel}
35400      */
35401     getPanel : function(id){
35402         if(typeof id == "object"){ // must be panel obj
35403             return id;
35404         }
35405         return this.panels.get(id);
35406     },
35407     
35408     /**
35409      * Returns this regions position (north/south/east/west/center).
35410      * @return {String} 
35411      */
35412     getPosition: function(){
35413         return this.position;    
35414     }
35415 });/*
35416  * Based on:
35417  * Ext JS Library 1.1.1
35418  * Copyright(c) 2006-2007, Ext JS, LLC.
35419  *
35420  * Originally Released Under LGPL - original licence link has changed is not relivant.
35421  *
35422  * Fork - LGPL
35423  * <script type="text/javascript">
35424  */
35425  
35426 /**
35427  * @class Roo.bootstrap.layout.Region
35428  * @extends Roo.bootstrap.layout.Basic
35429  * This class represents a region in a layout manager.
35430  
35431  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35432  * @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})
35433  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35434  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35435  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35436  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35437  * @cfg {String}    title           The title for the region (overrides panel titles)
35438  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35439  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35440  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35441  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35442  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35443  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35444  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35445  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35446  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35447  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35448
35449  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35450  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35451  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35452  * @cfg {Number}    width           For East/West panels
35453  * @cfg {Number}    height          For North/South panels
35454  * @cfg {Boolean}   split           To show the splitter
35455  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35456  * 
35457  * @cfg {string}   cls             Extra CSS classes to add to region
35458  * 
35459  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35460  * @cfg {string}   region  the region that it inhabits..
35461  *
35462
35463  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35464  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35465
35466  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35467  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35468  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35469  */
35470 Roo.bootstrap.layout.Region = function(config)
35471 {
35472     this.applyConfig(config);
35473
35474     var mgr = config.mgr;
35475     var pos = config.region;
35476     config.skipConfig = true;
35477     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35478     
35479     if (mgr.el) {
35480         this.onRender(mgr.el);   
35481     }
35482      
35483     this.visible = true;
35484     this.collapsed = false;
35485     this.unrendered_panels = [];
35486 };
35487
35488 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35489
35490     position: '', // set by wrapper (eg. north/south etc..)
35491     unrendered_panels : null,  // unrendered panels.
35492     createBody : function(){
35493         /** This region's body element 
35494         * @type Roo.Element */
35495         this.bodyEl = this.el.createChild({
35496                 tag: "div",
35497                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35498         });
35499     },
35500
35501     onRender: function(ctr, pos)
35502     {
35503         var dh = Roo.DomHelper;
35504         /** This region's container element 
35505         * @type Roo.Element */
35506         this.el = dh.append(ctr.dom, {
35507                 tag: "div",
35508                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35509             }, true);
35510         /** This region's title element 
35511         * @type Roo.Element */
35512     
35513         this.titleEl = dh.append(this.el.dom,
35514             {
35515                     tag: "div",
35516                     unselectable: "on",
35517                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35518                     children:[
35519                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35520                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35521                     ]}, true);
35522         
35523         this.titleEl.enableDisplayMode();
35524         /** This region's title text element 
35525         * @type HTMLElement */
35526         this.titleTextEl = this.titleEl.dom.firstChild;
35527         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35528         /*
35529         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35530         this.closeBtn.enableDisplayMode();
35531         this.closeBtn.on("click", this.closeClicked, this);
35532         this.closeBtn.hide();
35533     */
35534         this.createBody(this.config);
35535         if(this.config.hideWhenEmpty){
35536             this.hide();
35537             this.on("paneladded", this.validateVisibility, this);
35538             this.on("panelremoved", this.validateVisibility, this);
35539         }
35540         if(this.autoScroll){
35541             this.bodyEl.setStyle("overflow", "auto");
35542         }else{
35543             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35544         }
35545         //if(c.titlebar !== false){
35546             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35547                 this.titleEl.hide();
35548             }else{
35549                 this.titleEl.show();
35550                 if(this.config.title){
35551                     this.titleTextEl.innerHTML = this.config.title;
35552                 }
35553             }
35554         //}
35555         if(this.config.collapsed){
35556             this.collapse(true);
35557         }
35558         if(this.config.hidden){
35559             this.hide();
35560         }
35561         
35562         if (this.unrendered_panels && this.unrendered_panels.length) {
35563             for (var i =0;i< this.unrendered_panels.length; i++) {
35564                 this.add(this.unrendered_panels[i]);
35565             }
35566             this.unrendered_panels = null;
35567             
35568         }
35569         
35570     },
35571     
35572     applyConfig : function(c)
35573     {
35574         /*
35575          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35576             var dh = Roo.DomHelper;
35577             if(c.titlebar !== false){
35578                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35579                 this.collapseBtn.on("click", this.collapse, this);
35580                 this.collapseBtn.enableDisplayMode();
35581                 /*
35582                 if(c.showPin === true || this.showPin){
35583                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35584                     this.stickBtn.enableDisplayMode();
35585                     this.stickBtn.on("click", this.expand, this);
35586                     this.stickBtn.hide();
35587                 }
35588                 
35589             }
35590             */
35591             /** This region's collapsed element
35592             * @type Roo.Element */
35593             /*
35594              *
35595             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35596                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35597             ]}, true);
35598             
35599             if(c.floatable !== false){
35600                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35601                this.collapsedEl.on("click", this.collapseClick, this);
35602             }
35603
35604             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35605                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35606                    id: "message", unselectable: "on", style:{"float":"left"}});
35607                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35608              }
35609             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35610             this.expandBtn.on("click", this.expand, this);
35611             
35612         }
35613         
35614         if(this.collapseBtn){
35615             this.collapseBtn.setVisible(c.collapsible == true);
35616         }
35617         
35618         this.cmargins = c.cmargins || this.cmargins ||
35619                          (this.position == "west" || this.position == "east" ?
35620                              {top: 0, left: 2, right:2, bottom: 0} :
35621                              {top: 2, left: 0, right:0, bottom: 2});
35622         */
35623         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35624         
35625         
35626         this.bottomTabs = c.tabPosition != "top";
35627         
35628         this.autoScroll = c.autoScroll || false;
35629         
35630         
35631        
35632         
35633         this.duration = c.duration || .30;
35634         this.slideDuration = c.slideDuration || .45;
35635         this.config = c;
35636        
35637     },
35638     /**
35639      * Returns true if this region is currently visible.
35640      * @return {Boolean}
35641      */
35642     isVisible : function(){
35643         return this.visible;
35644     },
35645
35646     /**
35647      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35648      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35649      */
35650     //setCollapsedTitle : function(title){
35651     //    title = title || "&#160;";
35652      //   if(this.collapsedTitleTextEl){
35653       //      this.collapsedTitleTextEl.innerHTML = title;
35654        // }
35655     //},
35656
35657     getBox : function(){
35658         var b;
35659       //  if(!this.collapsed){
35660             b = this.el.getBox(false, true);
35661        // }else{
35662           //  b = this.collapsedEl.getBox(false, true);
35663         //}
35664         return b;
35665     },
35666
35667     getMargins : function(){
35668         return this.margins;
35669         //return this.collapsed ? this.cmargins : this.margins;
35670     },
35671 /*
35672     highlight : function(){
35673         this.el.addClass("x-layout-panel-dragover");
35674     },
35675
35676     unhighlight : function(){
35677         this.el.removeClass("x-layout-panel-dragover");
35678     },
35679 */
35680     updateBox : function(box)
35681     {
35682         if (!this.bodyEl) {
35683             return; // not rendered yet..
35684         }
35685         
35686         this.box = box;
35687         if(!this.collapsed){
35688             this.el.dom.style.left = box.x + "px";
35689             this.el.dom.style.top = box.y + "px";
35690             this.updateBody(box.width, box.height);
35691         }else{
35692             this.collapsedEl.dom.style.left = box.x + "px";
35693             this.collapsedEl.dom.style.top = box.y + "px";
35694             this.collapsedEl.setSize(box.width, box.height);
35695         }
35696         if(this.tabs){
35697             this.tabs.autoSizeTabs();
35698         }
35699     },
35700
35701     updateBody : function(w, h)
35702     {
35703         if(w !== null){
35704             this.el.setWidth(w);
35705             w -= this.el.getBorderWidth("rl");
35706             if(this.config.adjustments){
35707                 w += this.config.adjustments[0];
35708             }
35709         }
35710         if(h !== null && h > 0){
35711             this.el.setHeight(h);
35712             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35713             h -= this.el.getBorderWidth("tb");
35714             if(this.config.adjustments){
35715                 h += this.config.adjustments[1];
35716             }
35717             this.bodyEl.setHeight(h);
35718             if(this.tabs){
35719                 h = this.tabs.syncHeight(h);
35720             }
35721         }
35722         if(this.panelSize){
35723             w = w !== null ? w : this.panelSize.width;
35724             h = h !== null ? h : this.panelSize.height;
35725         }
35726         if(this.activePanel){
35727             var el = this.activePanel.getEl();
35728             w = w !== null ? w : el.getWidth();
35729             h = h !== null ? h : el.getHeight();
35730             this.panelSize = {width: w, height: h};
35731             this.activePanel.setSize(w, h);
35732         }
35733         if(Roo.isIE && this.tabs){
35734             this.tabs.el.repaint();
35735         }
35736     },
35737
35738     /**
35739      * Returns the container element for this region.
35740      * @return {Roo.Element}
35741      */
35742     getEl : function(){
35743         return this.el;
35744     },
35745
35746     /**
35747      * Hides this region.
35748      */
35749     hide : function(){
35750         //if(!this.collapsed){
35751             this.el.dom.style.left = "-2000px";
35752             this.el.hide();
35753         //}else{
35754          //   this.collapsedEl.dom.style.left = "-2000px";
35755          //   this.collapsedEl.hide();
35756        // }
35757         this.visible = false;
35758         this.fireEvent("visibilitychange", this, false);
35759     },
35760
35761     /**
35762      * Shows this region if it was previously hidden.
35763      */
35764     show : function(){
35765         //if(!this.collapsed){
35766             this.el.show();
35767         //}else{
35768         //    this.collapsedEl.show();
35769        // }
35770         this.visible = true;
35771         this.fireEvent("visibilitychange", this, true);
35772     },
35773 /*
35774     closeClicked : function(){
35775         if(this.activePanel){
35776             this.remove(this.activePanel);
35777         }
35778     },
35779
35780     collapseClick : function(e){
35781         if(this.isSlid){
35782            e.stopPropagation();
35783            this.slideIn();
35784         }else{
35785            e.stopPropagation();
35786            this.slideOut();
35787         }
35788     },
35789 */
35790     /**
35791      * Collapses this region.
35792      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35793      */
35794     /*
35795     collapse : function(skipAnim, skipCheck = false){
35796         if(this.collapsed) {
35797             return;
35798         }
35799         
35800         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35801             
35802             this.collapsed = true;
35803             if(this.split){
35804                 this.split.el.hide();
35805             }
35806             if(this.config.animate && skipAnim !== true){
35807                 this.fireEvent("invalidated", this);
35808                 this.animateCollapse();
35809             }else{
35810                 this.el.setLocation(-20000,-20000);
35811                 this.el.hide();
35812                 this.collapsedEl.show();
35813                 this.fireEvent("collapsed", this);
35814                 this.fireEvent("invalidated", this);
35815             }
35816         }
35817         
35818     },
35819 */
35820     animateCollapse : function(){
35821         // overridden
35822     },
35823
35824     /**
35825      * Expands this region if it was previously collapsed.
35826      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35827      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35828      */
35829     /*
35830     expand : function(e, skipAnim){
35831         if(e) {
35832             e.stopPropagation();
35833         }
35834         if(!this.collapsed || this.el.hasActiveFx()) {
35835             return;
35836         }
35837         if(this.isSlid){
35838             this.afterSlideIn();
35839             skipAnim = true;
35840         }
35841         this.collapsed = false;
35842         if(this.config.animate && skipAnim !== true){
35843             this.animateExpand();
35844         }else{
35845             this.el.show();
35846             if(this.split){
35847                 this.split.el.show();
35848             }
35849             this.collapsedEl.setLocation(-2000,-2000);
35850             this.collapsedEl.hide();
35851             this.fireEvent("invalidated", this);
35852             this.fireEvent("expanded", this);
35853         }
35854     },
35855 */
35856     animateExpand : function(){
35857         // overridden
35858     },
35859
35860     initTabs : function()
35861     {
35862         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35863         
35864         var ts = new Roo.bootstrap.panel.Tabs({
35865                 el: this.bodyEl.dom,
35866                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35867                 disableTooltips: this.config.disableTabTips,
35868                 toolbar : this.config.toolbar
35869             });
35870         
35871         if(this.config.hideTabs){
35872             ts.stripWrap.setDisplayed(false);
35873         }
35874         this.tabs = ts;
35875         ts.resizeTabs = this.config.resizeTabs === true;
35876         ts.minTabWidth = this.config.minTabWidth || 40;
35877         ts.maxTabWidth = this.config.maxTabWidth || 250;
35878         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35879         ts.monitorResize = false;
35880         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35881         ts.bodyEl.addClass('roo-layout-tabs-body');
35882         this.panels.each(this.initPanelAsTab, this);
35883     },
35884
35885     initPanelAsTab : function(panel){
35886         var ti = this.tabs.addTab(
35887             panel.getEl().id,
35888             panel.getTitle(),
35889             null,
35890             this.config.closeOnTab && panel.isClosable(),
35891             panel.tpl
35892         );
35893         if(panel.tabTip !== undefined){
35894             ti.setTooltip(panel.tabTip);
35895         }
35896         ti.on("activate", function(){
35897               this.setActivePanel(panel);
35898         }, this);
35899         
35900         if(this.config.closeOnTab){
35901             ti.on("beforeclose", function(t, e){
35902                 e.cancel = true;
35903                 this.remove(panel);
35904             }, this);
35905         }
35906         
35907         panel.tabItem = ti;
35908         
35909         return ti;
35910     },
35911
35912     updatePanelTitle : function(panel, title)
35913     {
35914         if(this.activePanel == panel){
35915             this.updateTitle(title);
35916         }
35917         if(this.tabs){
35918             var ti = this.tabs.getTab(panel.getEl().id);
35919             ti.setText(title);
35920             if(panel.tabTip !== undefined){
35921                 ti.setTooltip(panel.tabTip);
35922             }
35923         }
35924     },
35925
35926     updateTitle : function(title){
35927         if(this.titleTextEl && !this.config.title){
35928             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35929         }
35930     },
35931
35932     setActivePanel : function(panel)
35933     {
35934         panel = this.getPanel(panel);
35935         if(this.activePanel && this.activePanel != panel){
35936             if(this.activePanel.setActiveState(false) === false){
35937                 return;
35938             }
35939         }
35940         this.activePanel = panel;
35941         panel.setActiveState(true);
35942         if(this.panelSize){
35943             panel.setSize(this.panelSize.width, this.panelSize.height);
35944         }
35945         if(this.closeBtn){
35946             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35947         }
35948         this.updateTitle(panel.getTitle());
35949         if(this.tabs){
35950             this.fireEvent("invalidated", this);
35951         }
35952         this.fireEvent("panelactivated", this, panel);
35953     },
35954
35955     /**
35956      * Shows the specified panel.
35957      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35958      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35959      */
35960     showPanel : function(panel)
35961     {
35962         panel = this.getPanel(panel);
35963         if(panel){
35964             if(this.tabs){
35965                 var tab = this.tabs.getTab(panel.getEl().id);
35966                 if(tab.isHidden()){
35967                     this.tabs.unhideTab(tab.id);
35968                 }
35969                 tab.activate();
35970             }else{
35971                 this.setActivePanel(panel);
35972             }
35973         }
35974         return panel;
35975     },
35976
35977     /**
35978      * Get the active panel for this region.
35979      * @return {Roo.ContentPanel} The active panel or null
35980      */
35981     getActivePanel : function(){
35982         return this.activePanel;
35983     },
35984
35985     validateVisibility : function(){
35986         if(this.panels.getCount() < 1){
35987             this.updateTitle("&#160;");
35988             this.closeBtn.hide();
35989             this.hide();
35990         }else{
35991             if(!this.isVisible()){
35992                 this.show();
35993             }
35994         }
35995     },
35996
35997     /**
35998      * Adds the passed ContentPanel(s) to this region.
35999      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36000      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36001      */
36002     add : function(panel)
36003     {
36004         if(arguments.length > 1){
36005             for(var i = 0, len = arguments.length; i < len; i++) {
36006                 this.add(arguments[i]);
36007             }
36008             return null;
36009         }
36010         
36011         // if we have not been rendered yet, then we can not really do much of this..
36012         if (!this.bodyEl) {
36013             this.unrendered_panels.push(panel);
36014             return panel;
36015         }
36016         
36017         
36018         
36019         
36020         if(this.hasPanel(panel)){
36021             this.showPanel(panel);
36022             return panel;
36023         }
36024         panel.setRegion(this);
36025         this.panels.add(panel);
36026        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36027             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36028             // and hide them... ???
36029             this.bodyEl.dom.appendChild(panel.getEl().dom);
36030             if(panel.background !== true){
36031                 this.setActivePanel(panel);
36032             }
36033             this.fireEvent("paneladded", this, panel);
36034             return panel;
36035         }
36036         */
36037         if(!this.tabs){
36038             this.initTabs();
36039         }else{
36040             this.initPanelAsTab(panel);
36041         }
36042         
36043         
36044         if(panel.background !== true){
36045             this.tabs.activate(panel.getEl().id);
36046         }
36047         this.fireEvent("paneladded", this, panel);
36048         return panel;
36049     },
36050
36051     /**
36052      * Hides the tab for the specified panel.
36053      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36054      */
36055     hidePanel : function(panel){
36056         if(this.tabs && (panel = this.getPanel(panel))){
36057             this.tabs.hideTab(panel.getEl().id);
36058         }
36059     },
36060
36061     /**
36062      * Unhides the tab for a previously hidden panel.
36063      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36064      */
36065     unhidePanel : function(panel){
36066         if(this.tabs && (panel = this.getPanel(panel))){
36067             this.tabs.unhideTab(panel.getEl().id);
36068         }
36069     },
36070
36071     clearPanels : function(){
36072         while(this.panels.getCount() > 0){
36073              this.remove(this.panels.first());
36074         }
36075     },
36076
36077     /**
36078      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36079      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36080      * @param {Boolean} preservePanel Overrides the config preservePanel option
36081      * @return {Roo.ContentPanel} The panel that was removed
36082      */
36083     remove : function(panel, preservePanel)
36084     {
36085         panel = this.getPanel(panel);
36086         if(!panel){
36087             return null;
36088         }
36089         var e = {};
36090         this.fireEvent("beforeremove", this, panel, e);
36091         if(e.cancel === true){
36092             return null;
36093         }
36094         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36095         var panelId = panel.getId();
36096         this.panels.removeKey(panelId);
36097         if(preservePanel){
36098             document.body.appendChild(panel.getEl().dom);
36099         }
36100         if(this.tabs){
36101             this.tabs.removeTab(panel.getEl().id);
36102         }else if (!preservePanel){
36103             this.bodyEl.dom.removeChild(panel.getEl().dom);
36104         }
36105         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36106             var p = this.panels.first();
36107             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36108             tempEl.appendChild(p.getEl().dom);
36109             this.bodyEl.update("");
36110             this.bodyEl.dom.appendChild(p.getEl().dom);
36111             tempEl = null;
36112             this.updateTitle(p.getTitle());
36113             this.tabs = null;
36114             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36115             this.setActivePanel(p);
36116         }
36117         panel.setRegion(null);
36118         if(this.activePanel == panel){
36119             this.activePanel = null;
36120         }
36121         if(this.config.autoDestroy !== false && preservePanel !== true){
36122             try{panel.destroy();}catch(e){}
36123         }
36124         this.fireEvent("panelremoved", this, panel);
36125         return panel;
36126     },
36127
36128     /**
36129      * Returns the TabPanel component used by this region
36130      * @return {Roo.TabPanel}
36131      */
36132     getTabs : function(){
36133         return this.tabs;
36134     },
36135
36136     createTool : function(parentEl, className){
36137         var btn = Roo.DomHelper.append(parentEl, {
36138             tag: "div",
36139             cls: "x-layout-tools-button",
36140             children: [ {
36141                 tag: "div",
36142                 cls: "roo-layout-tools-button-inner " + className,
36143                 html: "&#160;"
36144             }]
36145         }, true);
36146         btn.addClassOnOver("roo-layout-tools-button-over");
36147         return btn;
36148     }
36149 });/*
36150  * Based on:
36151  * Ext JS Library 1.1.1
36152  * Copyright(c) 2006-2007, Ext JS, LLC.
36153  *
36154  * Originally Released Under LGPL - original licence link has changed is not relivant.
36155  *
36156  * Fork - LGPL
36157  * <script type="text/javascript">
36158  */
36159  
36160
36161
36162 /**
36163  * @class Roo.SplitLayoutRegion
36164  * @extends Roo.LayoutRegion
36165  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36166  */
36167 Roo.bootstrap.layout.Split = function(config){
36168     this.cursor = config.cursor;
36169     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36170 };
36171
36172 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36173 {
36174     splitTip : "Drag to resize.",
36175     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36176     useSplitTips : false,
36177
36178     applyConfig : function(config){
36179         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36180     },
36181     
36182     onRender : function(ctr,pos) {
36183         
36184         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36185         if(!this.config.split){
36186             return;
36187         }
36188         if(!this.split){
36189             
36190             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36191                             tag: "div",
36192                             id: this.el.id + "-split",
36193                             cls: "roo-layout-split roo-layout-split-"+this.position,
36194                             html: "&#160;"
36195             });
36196             /** The SplitBar for this region 
36197             * @type Roo.SplitBar */
36198             // does not exist yet...
36199             Roo.log([this.position, this.orientation]);
36200             
36201             this.split = new Roo.bootstrap.SplitBar({
36202                 dragElement : splitEl,
36203                 resizingElement: this.el,
36204                 orientation : this.orientation
36205             });
36206             
36207             this.split.on("moved", this.onSplitMove, this);
36208             this.split.useShim = this.config.useShim === true;
36209             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36210             if(this.useSplitTips){
36211                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36212             }
36213             //if(config.collapsible){
36214             //    this.split.el.on("dblclick", this.collapse,  this);
36215             //}
36216         }
36217         if(typeof this.config.minSize != "undefined"){
36218             this.split.minSize = this.config.minSize;
36219         }
36220         if(typeof this.config.maxSize != "undefined"){
36221             this.split.maxSize = this.config.maxSize;
36222         }
36223         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36224             this.hideSplitter();
36225         }
36226         
36227     },
36228
36229     getHMaxSize : function(){
36230          var cmax = this.config.maxSize || 10000;
36231          var center = this.mgr.getRegion("center");
36232          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36233     },
36234
36235     getVMaxSize : function(){
36236          var cmax = this.config.maxSize || 10000;
36237          var center = this.mgr.getRegion("center");
36238          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36239     },
36240
36241     onSplitMove : function(split, newSize){
36242         this.fireEvent("resized", this, newSize);
36243     },
36244     
36245     /** 
36246      * Returns the {@link Roo.SplitBar} for this region.
36247      * @return {Roo.SplitBar}
36248      */
36249     getSplitBar : function(){
36250         return this.split;
36251     },
36252     
36253     hide : function(){
36254         this.hideSplitter();
36255         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36256     },
36257
36258     hideSplitter : function(){
36259         if(this.split){
36260             this.split.el.setLocation(-2000,-2000);
36261             this.split.el.hide();
36262         }
36263     },
36264
36265     show : function(){
36266         if(this.split){
36267             this.split.el.show();
36268         }
36269         Roo.bootstrap.layout.Split.superclass.show.call(this);
36270     },
36271     
36272     beforeSlide: function(){
36273         if(Roo.isGecko){// firefox overflow auto bug workaround
36274             this.bodyEl.clip();
36275             if(this.tabs) {
36276                 this.tabs.bodyEl.clip();
36277             }
36278             if(this.activePanel){
36279                 this.activePanel.getEl().clip();
36280                 
36281                 if(this.activePanel.beforeSlide){
36282                     this.activePanel.beforeSlide();
36283                 }
36284             }
36285         }
36286     },
36287     
36288     afterSlide : function(){
36289         if(Roo.isGecko){// firefox overflow auto bug workaround
36290             this.bodyEl.unclip();
36291             if(this.tabs) {
36292                 this.tabs.bodyEl.unclip();
36293             }
36294             if(this.activePanel){
36295                 this.activePanel.getEl().unclip();
36296                 if(this.activePanel.afterSlide){
36297                     this.activePanel.afterSlide();
36298                 }
36299             }
36300         }
36301     },
36302
36303     initAutoHide : function(){
36304         if(this.autoHide !== false){
36305             if(!this.autoHideHd){
36306                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36307                 this.autoHideHd = {
36308                     "mouseout": function(e){
36309                         if(!e.within(this.el, true)){
36310                             st.delay(500);
36311                         }
36312                     },
36313                     "mouseover" : function(e){
36314                         st.cancel();
36315                     },
36316                     scope : this
36317                 };
36318             }
36319             this.el.on(this.autoHideHd);
36320         }
36321     },
36322
36323     clearAutoHide : function(){
36324         if(this.autoHide !== false){
36325             this.el.un("mouseout", this.autoHideHd.mouseout);
36326             this.el.un("mouseover", this.autoHideHd.mouseover);
36327         }
36328     },
36329
36330     clearMonitor : function(){
36331         Roo.get(document).un("click", this.slideInIf, this);
36332     },
36333
36334     // these names are backwards but not changed for compat
36335     slideOut : function(){
36336         if(this.isSlid || this.el.hasActiveFx()){
36337             return;
36338         }
36339         this.isSlid = true;
36340         if(this.collapseBtn){
36341             this.collapseBtn.hide();
36342         }
36343         this.closeBtnState = this.closeBtn.getStyle('display');
36344         this.closeBtn.hide();
36345         if(this.stickBtn){
36346             this.stickBtn.show();
36347         }
36348         this.el.show();
36349         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36350         this.beforeSlide();
36351         this.el.setStyle("z-index", 10001);
36352         this.el.slideIn(this.getSlideAnchor(), {
36353             callback: function(){
36354                 this.afterSlide();
36355                 this.initAutoHide();
36356                 Roo.get(document).on("click", this.slideInIf, this);
36357                 this.fireEvent("slideshow", this);
36358             },
36359             scope: this,
36360             block: true
36361         });
36362     },
36363
36364     afterSlideIn : function(){
36365         this.clearAutoHide();
36366         this.isSlid = false;
36367         this.clearMonitor();
36368         this.el.setStyle("z-index", "");
36369         if(this.collapseBtn){
36370             this.collapseBtn.show();
36371         }
36372         this.closeBtn.setStyle('display', this.closeBtnState);
36373         if(this.stickBtn){
36374             this.stickBtn.hide();
36375         }
36376         this.fireEvent("slidehide", this);
36377     },
36378
36379     slideIn : function(cb){
36380         if(!this.isSlid || this.el.hasActiveFx()){
36381             Roo.callback(cb);
36382             return;
36383         }
36384         this.isSlid = false;
36385         this.beforeSlide();
36386         this.el.slideOut(this.getSlideAnchor(), {
36387             callback: function(){
36388                 this.el.setLeftTop(-10000, -10000);
36389                 this.afterSlide();
36390                 this.afterSlideIn();
36391                 Roo.callback(cb);
36392             },
36393             scope: this,
36394             block: true
36395         });
36396     },
36397     
36398     slideInIf : function(e){
36399         if(!e.within(this.el)){
36400             this.slideIn();
36401         }
36402     },
36403
36404     animateCollapse : function(){
36405         this.beforeSlide();
36406         this.el.setStyle("z-index", 20000);
36407         var anchor = this.getSlideAnchor();
36408         this.el.slideOut(anchor, {
36409             callback : function(){
36410                 this.el.setStyle("z-index", "");
36411                 this.collapsedEl.slideIn(anchor, {duration:.3});
36412                 this.afterSlide();
36413                 this.el.setLocation(-10000,-10000);
36414                 this.el.hide();
36415                 this.fireEvent("collapsed", this);
36416             },
36417             scope: this,
36418             block: true
36419         });
36420     },
36421
36422     animateExpand : function(){
36423         this.beforeSlide();
36424         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36425         this.el.setStyle("z-index", 20000);
36426         this.collapsedEl.hide({
36427             duration:.1
36428         });
36429         this.el.slideIn(this.getSlideAnchor(), {
36430             callback : function(){
36431                 this.el.setStyle("z-index", "");
36432                 this.afterSlide();
36433                 if(this.split){
36434                     this.split.el.show();
36435                 }
36436                 this.fireEvent("invalidated", this);
36437                 this.fireEvent("expanded", this);
36438             },
36439             scope: this,
36440             block: true
36441         });
36442     },
36443
36444     anchors : {
36445         "west" : "left",
36446         "east" : "right",
36447         "north" : "top",
36448         "south" : "bottom"
36449     },
36450
36451     sanchors : {
36452         "west" : "l",
36453         "east" : "r",
36454         "north" : "t",
36455         "south" : "b"
36456     },
36457
36458     canchors : {
36459         "west" : "tl-tr",
36460         "east" : "tr-tl",
36461         "north" : "tl-bl",
36462         "south" : "bl-tl"
36463     },
36464
36465     getAnchor : function(){
36466         return this.anchors[this.position];
36467     },
36468
36469     getCollapseAnchor : function(){
36470         return this.canchors[this.position];
36471     },
36472
36473     getSlideAnchor : function(){
36474         return this.sanchors[this.position];
36475     },
36476
36477     getAlignAdj : function(){
36478         var cm = this.cmargins;
36479         switch(this.position){
36480             case "west":
36481                 return [0, 0];
36482             break;
36483             case "east":
36484                 return [0, 0];
36485             break;
36486             case "north":
36487                 return [0, 0];
36488             break;
36489             case "south":
36490                 return [0, 0];
36491             break;
36492         }
36493     },
36494
36495     getExpandAdj : function(){
36496         var c = this.collapsedEl, cm = this.cmargins;
36497         switch(this.position){
36498             case "west":
36499                 return [-(cm.right+c.getWidth()+cm.left), 0];
36500             break;
36501             case "east":
36502                 return [cm.right+c.getWidth()+cm.left, 0];
36503             break;
36504             case "north":
36505                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36506             break;
36507             case "south":
36508                 return [0, cm.top+cm.bottom+c.getHeight()];
36509             break;
36510         }
36511     }
36512 });/*
36513  * Based on:
36514  * Ext JS Library 1.1.1
36515  * Copyright(c) 2006-2007, Ext JS, LLC.
36516  *
36517  * Originally Released Under LGPL - original licence link has changed is not relivant.
36518  *
36519  * Fork - LGPL
36520  * <script type="text/javascript">
36521  */
36522 /*
36523  * These classes are private internal classes
36524  */
36525 Roo.bootstrap.layout.Center = function(config){
36526     config.region = "center";
36527     Roo.bootstrap.layout.Region.call(this, config);
36528     this.visible = true;
36529     this.minWidth = config.minWidth || 20;
36530     this.minHeight = config.minHeight || 20;
36531 };
36532
36533 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36534     hide : function(){
36535         // center panel can't be hidden
36536     },
36537     
36538     show : function(){
36539         // center panel can't be hidden
36540     },
36541     
36542     getMinWidth: function(){
36543         return this.minWidth;
36544     },
36545     
36546     getMinHeight: function(){
36547         return this.minHeight;
36548     }
36549 });
36550
36551
36552
36553
36554  
36555
36556
36557
36558
36559
36560 Roo.bootstrap.layout.North = function(config)
36561 {
36562     config.region = 'north';
36563     config.cursor = 'n-resize';
36564     
36565     Roo.bootstrap.layout.Split.call(this, config);
36566     
36567     
36568     if(this.split){
36569         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36570         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36571         this.split.el.addClass("roo-layout-split-v");
36572     }
36573     var size = config.initialSize || config.height;
36574     if(typeof size != "undefined"){
36575         this.el.setHeight(size);
36576     }
36577 };
36578 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36579 {
36580     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36581     
36582     
36583     
36584     getBox : function(){
36585         if(this.collapsed){
36586             return this.collapsedEl.getBox();
36587         }
36588         var box = this.el.getBox();
36589         if(this.split){
36590             box.height += this.split.el.getHeight();
36591         }
36592         return box;
36593     },
36594     
36595     updateBox : function(box){
36596         if(this.split && !this.collapsed){
36597             box.height -= this.split.el.getHeight();
36598             this.split.el.setLeft(box.x);
36599             this.split.el.setTop(box.y+box.height);
36600             this.split.el.setWidth(box.width);
36601         }
36602         if(this.collapsed){
36603             this.updateBody(box.width, null);
36604         }
36605         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36606     }
36607 });
36608
36609
36610
36611
36612
36613 Roo.bootstrap.layout.South = function(config){
36614     config.region = 'south';
36615     config.cursor = 's-resize';
36616     Roo.bootstrap.layout.Split.call(this, config);
36617     if(this.split){
36618         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36619         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36620         this.split.el.addClass("roo-layout-split-v");
36621     }
36622     var size = config.initialSize || config.height;
36623     if(typeof size != "undefined"){
36624         this.el.setHeight(size);
36625     }
36626 };
36627
36628 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36629     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36630     getBox : function(){
36631         if(this.collapsed){
36632             return this.collapsedEl.getBox();
36633         }
36634         var box = this.el.getBox();
36635         if(this.split){
36636             var sh = this.split.el.getHeight();
36637             box.height += sh;
36638             box.y -= sh;
36639         }
36640         return box;
36641     },
36642     
36643     updateBox : function(box){
36644         if(this.split && !this.collapsed){
36645             var sh = this.split.el.getHeight();
36646             box.height -= sh;
36647             box.y += sh;
36648             this.split.el.setLeft(box.x);
36649             this.split.el.setTop(box.y-sh);
36650             this.split.el.setWidth(box.width);
36651         }
36652         if(this.collapsed){
36653             this.updateBody(box.width, null);
36654         }
36655         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36656     }
36657 });
36658
36659 Roo.bootstrap.layout.East = function(config){
36660     config.region = "east";
36661     config.cursor = "e-resize";
36662     Roo.bootstrap.layout.Split.call(this, config);
36663     if(this.split){
36664         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36665         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36666         this.split.el.addClass("roo-layout-split-h");
36667     }
36668     var size = config.initialSize || config.width;
36669     if(typeof size != "undefined"){
36670         this.el.setWidth(size);
36671     }
36672 };
36673 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36674     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36675     getBox : function(){
36676         if(this.collapsed){
36677             return this.collapsedEl.getBox();
36678         }
36679         var box = this.el.getBox();
36680         if(this.split){
36681             var sw = this.split.el.getWidth();
36682             box.width += sw;
36683             box.x -= sw;
36684         }
36685         return box;
36686     },
36687
36688     updateBox : function(box){
36689         if(this.split && !this.collapsed){
36690             var sw = this.split.el.getWidth();
36691             box.width -= sw;
36692             this.split.el.setLeft(box.x);
36693             this.split.el.setTop(box.y);
36694             this.split.el.setHeight(box.height);
36695             box.x += sw;
36696         }
36697         if(this.collapsed){
36698             this.updateBody(null, box.height);
36699         }
36700         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36701     }
36702 });
36703
36704 Roo.bootstrap.layout.West = function(config){
36705     config.region = "west";
36706     config.cursor = "w-resize";
36707     
36708     Roo.bootstrap.layout.Split.call(this, config);
36709     if(this.split){
36710         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36711         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36712         this.split.el.addClass("roo-layout-split-h");
36713     }
36714     
36715 };
36716 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36717     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36718     
36719     onRender: function(ctr, pos)
36720     {
36721         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36722         var size = this.config.initialSize || this.config.width;
36723         if(typeof size != "undefined"){
36724             this.el.setWidth(size);
36725         }
36726     },
36727     
36728     getBox : function(){
36729         if(this.collapsed){
36730             return this.collapsedEl.getBox();
36731         }
36732         var box = this.el.getBox();
36733         if(this.split){
36734             box.width += this.split.el.getWidth();
36735         }
36736         return box;
36737     },
36738     
36739     updateBox : function(box){
36740         if(this.split && !this.collapsed){
36741             var sw = this.split.el.getWidth();
36742             box.width -= sw;
36743             this.split.el.setLeft(box.x+box.width);
36744             this.split.el.setTop(box.y);
36745             this.split.el.setHeight(box.height);
36746         }
36747         if(this.collapsed){
36748             this.updateBody(null, box.height);
36749         }
36750         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36751     }
36752 });
36753 Roo.namespace("Roo.bootstrap.panel");/*
36754  * Based on:
36755  * Ext JS Library 1.1.1
36756  * Copyright(c) 2006-2007, Ext JS, LLC.
36757  *
36758  * Originally Released Under LGPL - original licence link has changed is not relivant.
36759  *
36760  * Fork - LGPL
36761  * <script type="text/javascript">
36762  */
36763 /**
36764  * @class Roo.ContentPanel
36765  * @extends Roo.util.Observable
36766  * A basic ContentPanel element.
36767  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36768  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36769  * @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
36770  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36771  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36772  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36773  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36774  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36775  * @cfg {String} title          The title for this panel
36776  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36777  * @cfg {String} url            Calls {@link #setUrl} with this value
36778  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36779  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36780  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36781  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36782  * @cfg {Boolean} badges render the badges
36783
36784  * @constructor
36785  * Create a new ContentPanel.
36786  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36787  * @param {String/Object} config A string to set only the title or a config object
36788  * @param {String} content (optional) Set the HTML content for this panel
36789  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36790  */
36791 Roo.bootstrap.panel.Content = function( config){
36792     
36793     this.tpl = config.tpl || false;
36794     
36795     var el = config.el;
36796     var content = config.content;
36797
36798     if(config.autoCreate){ // xtype is available if this is called from factory
36799         el = Roo.id();
36800     }
36801     this.el = Roo.get(el);
36802     if(!this.el && config && config.autoCreate){
36803         if(typeof config.autoCreate == "object"){
36804             if(!config.autoCreate.id){
36805                 config.autoCreate.id = config.id||el;
36806             }
36807             this.el = Roo.DomHelper.append(document.body,
36808                         config.autoCreate, true);
36809         }else{
36810             var elcfg =  {   tag: "div",
36811                             cls: "roo-layout-inactive-content",
36812                             id: config.id||el
36813                             };
36814             if (config.html) {
36815                 elcfg.html = config.html;
36816                 
36817             }
36818                         
36819             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36820         }
36821     } 
36822     this.closable = false;
36823     this.loaded = false;
36824     this.active = false;
36825    
36826       
36827     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36828         
36829         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36830         
36831         this.wrapEl = this.el; //this.el.wrap();
36832         var ti = [];
36833         if (config.toolbar.items) {
36834             ti = config.toolbar.items ;
36835             delete config.toolbar.items ;
36836         }
36837         
36838         var nitems = [];
36839         this.toolbar.render(this.wrapEl, 'before');
36840         for(var i =0;i < ti.length;i++) {
36841           //  Roo.log(['add child', items[i]]);
36842             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36843         }
36844         this.toolbar.items = nitems;
36845         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36846         delete config.toolbar;
36847         
36848     }
36849     /*
36850     // xtype created footer. - not sure if will work as we normally have to render first..
36851     if (this.footer && !this.footer.el && this.footer.xtype) {
36852         if (!this.wrapEl) {
36853             this.wrapEl = this.el.wrap();
36854         }
36855     
36856         this.footer.container = this.wrapEl.createChild();
36857          
36858         this.footer = Roo.factory(this.footer, Roo);
36859         
36860     }
36861     */
36862     
36863      if(typeof config == "string"){
36864         this.title = config;
36865     }else{
36866         Roo.apply(this, config);
36867     }
36868     
36869     if(this.resizeEl){
36870         this.resizeEl = Roo.get(this.resizeEl, true);
36871     }else{
36872         this.resizeEl = this.el;
36873     }
36874     // handle view.xtype
36875     
36876  
36877     
36878     
36879     this.addEvents({
36880         /**
36881          * @event activate
36882          * Fires when this panel is activated. 
36883          * @param {Roo.ContentPanel} this
36884          */
36885         "activate" : true,
36886         /**
36887          * @event deactivate
36888          * Fires when this panel is activated. 
36889          * @param {Roo.ContentPanel} this
36890          */
36891         "deactivate" : true,
36892
36893         /**
36894          * @event resize
36895          * Fires when this panel is resized if fitToFrame is true.
36896          * @param {Roo.ContentPanel} this
36897          * @param {Number} width The width after any component adjustments
36898          * @param {Number} height The height after any component adjustments
36899          */
36900         "resize" : true,
36901         
36902          /**
36903          * @event render
36904          * Fires when this tab is created
36905          * @param {Roo.ContentPanel} this
36906          */
36907         "render" : true
36908         
36909         
36910         
36911     });
36912     
36913
36914     
36915     
36916     if(this.autoScroll){
36917         this.resizeEl.setStyle("overflow", "auto");
36918     } else {
36919         // fix randome scrolling
36920         //this.el.on('scroll', function() {
36921         //    Roo.log('fix random scolling');
36922         //    this.scrollTo('top',0); 
36923         //});
36924     }
36925     content = content || this.content;
36926     if(content){
36927         this.setContent(content);
36928     }
36929     if(config && config.url){
36930         this.setUrl(this.url, this.params, this.loadOnce);
36931     }
36932     
36933     
36934     
36935     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36936     
36937     if (this.view && typeof(this.view.xtype) != 'undefined') {
36938         this.view.el = this.el.appendChild(document.createElement("div"));
36939         this.view = Roo.factory(this.view); 
36940         this.view.render  &&  this.view.render(false, '');  
36941     }
36942     
36943     
36944     this.fireEvent('render', this);
36945 };
36946
36947 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36948     
36949     tabTip : '',
36950     
36951     setRegion : function(region){
36952         this.region = region;
36953         this.setActiveClass(region && !this.background);
36954     },
36955     
36956     
36957     setActiveClass: function(state)
36958     {
36959         if(state){
36960            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36961            this.el.setStyle('position','relative');
36962         }else{
36963            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36964            this.el.setStyle('position', 'absolute');
36965         } 
36966     },
36967     
36968     /**
36969      * Returns the toolbar for this Panel if one was configured. 
36970      * @return {Roo.Toolbar} 
36971      */
36972     getToolbar : function(){
36973         return this.toolbar;
36974     },
36975     
36976     setActiveState : function(active)
36977     {
36978         this.active = active;
36979         this.setActiveClass(active);
36980         if(!active){
36981             if(this.fireEvent("deactivate", this) === false){
36982                 return false;
36983             }
36984             return true;
36985         }
36986         this.fireEvent("activate", this);
36987         return true;
36988     },
36989     /**
36990      * Updates this panel's element
36991      * @param {String} content The new content
36992      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36993     */
36994     setContent : function(content, loadScripts){
36995         this.el.update(content, loadScripts);
36996     },
36997
36998     ignoreResize : function(w, h){
36999         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37000             return true;
37001         }else{
37002             this.lastSize = {width: w, height: h};
37003             return false;
37004         }
37005     },
37006     /**
37007      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37008      * @return {Roo.UpdateManager} The UpdateManager
37009      */
37010     getUpdateManager : function(){
37011         return this.el.getUpdateManager();
37012     },
37013      /**
37014      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37015      * @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:
37016 <pre><code>
37017 panel.load({
37018     url: "your-url.php",
37019     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37020     callback: yourFunction,
37021     scope: yourObject, //(optional scope)
37022     discardUrl: false,
37023     nocache: false,
37024     text: "Loading...",
37025     timeout: 30,
37026     scripts: false
37027 });
37028 </code></pre>
37029      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37030      * 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.
37031      * @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}
37032      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37033      * @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.
37034      * @return {Roo.ContentPanel} this
37035      */
37036     load : function(){
37037         var um = this.el.getUpdateManager();
37038         um.update.apply(um, arguments);
37039         return this;
37040     },
37041
37042
37043     /**
37044      * 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.
37045      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37046      * @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)
37047      * @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)
37048      * @return {Roo.UpdateManager} The UpdateManager
37049      */
37050     setUrl : function(url, params, loadOnce){
37051         if(this.refreshDelegate){
37052             this.removeListener("activate", this.refreshDelegate);
37053         }
37054         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37055         this.on("activate", this.refreshDelegate);
37056         return this.el.getUpdateManager();
37057     },
37058     
37059     _handleRefresh : function(url, params, loadOnce){
37060         if(!loadOnce || !this.loaded){
37061             var updater = this.el.getUpdateManager();
37062             updater.update(url, params, this._setLoaded.createDelegate(this));
37063         }
37064     },
37065     
37066     _setLoaded : function(){
37067         this.loaded = true;
37068     }, 
37069     
37070     /**
37071      * Returns this panel's id
37072      * @return {String} 
37073      */
37074     getId : function(){
37075         return this.el.id;
37076     },
37077     
37078     /** 
37079      * Returns this panel's element - used by regiosn to add.
37080      * @return {Roo.Element} 
37081      */
37082     getEl : function(){
37083         return this.wrapEl || this.el;
37084     },
37085     
37086    
37087     
37088     adjustForComponents : function(width, height)
37089     {
37090         //Roo.log('adjustForComponents ');
37091         if(this.resizeEl != this.el){
37092             width -= this.el.getFrameWidth('lr');
37093             height -= this.el.getFrameWidth('tb');
37094         }
37095         if(this.toolbar){
37096             var te = this.toolbar.getEl();
37097             te.setWidth(width);
37098             height -= te.getHeight();
37099         }
37100         if(this.footer){
37101             var te = this.footer.getEl();
37102             te.setWidth(width);
37103             height -= te.getHeight();
37104         }
37105         
37106         
37107         if(this.adjustments){
37108             width += this.adjustments[0];
37109             height += this.adjustments[1];
37110         }
37111         return {"width": width, "height": height};
37112     },
37113     
37114     setSize : function(width, height){
37115         if(this.fitToFrame && !this.ignoreResize(width, height)){
37116             if(this.fitContainer && this.resizeEl != this.el){
37117                 this.el.setSize(width, height);
37118             }
37119             var size = this.adjustForComponents(width, height);
37120             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37121             this.fireEvent('resize', this, size.width, size.height);
37122         }
37123     },
37124     
37125     /**
37126      * Returns this panel's title
37127      * @return {String} 
37128      */
37129     getTitle : function(){
37130         
37131         if (typeof(this.title) != 'object') {
37132             return this.title;
37133         }
37134         
37135         var t = '';
37136         for (var k in this.title) {
37137             if (!this.title.hasOwnProperty(k)) {
37138                 continue;
37139             }
37140             
37141             if (k.indexOf('-') >= 0) {
37142                 var s = k.split('-');
37143                 for (var i = 0; i<s.length; i++) {
37144                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37145                 }
37146             } else {
37147                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37148             }
37149         }
37150         return t;
37151     },
37152     
37153     /**
37154      * Set this panel's title
37155      * @param {String} title
37156      */
37157     setTitle : function(title){
37158         this.title = title;
37159         if(this.region){
37160             this.region.updatePanelTitle(this, title);
37161         }
37162     },
37163     
37164     /**
37165      * Returns true is this panel was configured to be closable
37166      * @return {Boolean} 
37167      */
37168     isClosable : function(){
37169         return this.closable;
37170     },
37171     
37172     beforeSlide : function(){
37173         this.el.clip();
37174         this.resizeEl.clip();
37175     },
37176     
37177     afterSlide : function(){
37178         this.el.unclip();
37179         this.resizeEl.unclip();
37180     },
37181     
37182     /**
37183      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37184      *   Will fail silently if the {@link #setUrl} method has not been called.
37185      *   This does not activate the panel, just updates its content.
37186      */
37187     refresh : function(){
37188         if(this.refreshDelegate){
37189            this.loaded = false;
37190            this.refreshDelegate();
37191         }
37192     },
37193     
37194     /**
37195      * Destroys this panel
37196      */
37197     destroy : function(){
37198         this.el.removeAllListeners();
37199         var tempEl = document.createElement("span");
37200         tempEl.appendChild(this.el.dom);
37201         tempEl.innerHTML = "";
37202         this.el.remove();
37203         this.el = null;
37204     },
37205     
37206     /**
37207      * form - if the content panel contains a form - this is a reference to it.
37208      * @type {Roo.form.Form}
37209      */
37210     form : false,
37211     /**
37212      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37213      *    This contains a reference to it.
37214      * @type {Roo.View}
37215      */
37216     view : false,
37217     
37218       /**
37219      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37220      * <pre><code>
37221
37222 layout.addxtype({
37223        xtype : 'Form',
37224        items: [ .... ]
37225    }
37226 );
37227
37228 </code></pre>
37229      * @param {Object} cfg Xtype definition of item to add.
37230      */
37231     
37232     
37233     getChildContainer: function () {
37234         return this.getEl();
37235     }
37236     
37237     
37238     /*
37239         var  ret = new Roo.factory(cfg);
37240         return ret;
37241         
37242         
37243         // add form..
37244         if (cfg.xtype.match(/^Form$/)) {
37245             
37246             var el;
37247             //if (this.footer) {
37248             //    el = this.footer.container.insertSibling(false, 'before');
37249             //} else {
37250                 el = this.el.createChild();
37251             //}
37252
37253             this.form = new  Roo.form.Form(cfg);
37254             
37255             
37256             if ( this.form.allItems.length) {
37257                 this.form.render(el.dom);
37258             }
37259             return this.form;
37260         }
37261         // should only have one of theses..
37262         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37263             // views.. should not be just added - used named prop 'view''
37264             
37265             cfg.el = this.el.appendChild(document.createElement("div"));
37266             // factory?
37267             
37268             var ret = new Roo.factory(cfg);
37269              
37270              ret.render && ret.render(false, ''); // render blank..
37271             this.view = ret;
37272             return ret;
37273         }
37274         return false;
37275     }
37276     \*/
37277 });
37278  
37279 /**
37280  * @class Roo.bootstrap.panel.Grid
37281  * @extends Roo.bootstrap.panel.Content
37282  * @constructor
37283  * Create a new GridPanel.
37284  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37285  * @param {Object} config A the config object
37286   
37287  */
37288
37289
37290
37291 Roo.bootstrap.panel.Grid = function(config)
37292 {
37293     
37294       
37295     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37296         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37297
37298     config.el = this.wrapper;
37299     //this.el = this.wrapper;
37300     
37301       if (config.container) {
37302         // ctor'ed from a Border/panel.grid
37303         
37304         
37305         this.wrapper.setStyle("overflow", "hidden");
37306         this.wrapper.addClass('roo-grid-container');
37307
37308     }
37309     
37310     
37311     if(config.toolbar){
37312         var tool_el = this.wrapper.createChild();    
37313         this.toolbar = Roo.factory(config.toolbar);
37314         var ti = [];
37315         if (config.toolbar.items) {
37316             ti = config.toolbar.items ;
37317             delete config.toolbar.items ;
37318         }
37319         
37320         var nitems = [];
37321         this.toolbar.render(tool_el);
37322         for(var i =0;i < ti.length;i++) {
37323           //  Roo.log(['add child', items[i]]);
37324             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37325         }
37326         this.toolbar.items = nitems;
37327         
37328         delete config.toolbar;
37329     }
37330     
37331     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37332     config.grid.scrollBody = true;;
37333     config.grid.monitorWindowResize = false; // turn off autosizing
37334     config.grid.autoHeight = false;
37335     config.grid.autoWidth = false;
37336     
37337     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37338     
37339     if (config.background) {
37340         // render grid on panel activation (if panel background)
37341         this.on('activate', function(gp) {
37342             if (!gp.grid.rendered) {
37343                 gp.grid.render(this.wrapper);
37344                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37345             }
37346         });
37347             
37348     } else {
37349         this.grid.render(this.wrapper);
37350         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37351
37352     }
37353     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37354     // ??? needed ??? config.el = this.wrapper;
37355     
37356     
37357     
37358   
37359     // xtype created footer. - not sure if will work as we normally have to render first..
37360     if (this.footer && !this.footer.el && this.footer.xtype) {
37361         
37362         var ctr = this.grid.getView().getFooterPanel(true);
37363         this.footer.dataSource = this.grid.dataSource;
37364         this.footer = Roo.factory(this.footer, Roo);
37365         this.footer.render(ctr);
37366         
37367     }
37368     
37369     
37370     
37371     
37372      
37373 };
37374
37375 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37376     getId : function(){
37377         return this.grid.id;
37378     },
37379     
37380     /**
37381      * Returns the grid for this panel
37382      * @return {Roo.bootstrap.Table} 
37383      */
37384     getGrid : function(){
37385         return this.grid;    
37386     },
37387     
37388     setSize : function(width, height){
37389         if(!this.ignoreResize(width, height)){
37390             var grid = this.grid;
37391             var size = this.adjustForComponents(width, height);
37392             var gridel = grid.getGridEl();
37393             gridel.setSize(size.width, size.height);
37394             /*
37395             var thd = grid.getGridEl().select('thead',true).first();
37396             var tbd = grid.getGridEl().select('tbody', true).first();
37397             if (tbd) {
37398                 tbd.setSize(width, height - thd.getHeight());
37399             }
37400             */
37401             grid.autoSize();
37402         }
37403     },
37404      
37405     
37406     
37407     beforeSlide : function(){
37408         this.grid.getView().scroller.clip();
37409     },
37410     
37411     afterSlide : function(){
37412         this.grid.getView().scroller.unclip();
37413     },
37414     
37415     destroy : function(){
37416         this.grid.destroy();
37417         delete this.grid;
37418         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37419     }
37420 });
37421
37422 /**
37423  * @class Roo.bootstrap.panel.Nest
37424  * @extends Roo.bootstrap.panel.Content
37425  * @constructor
37426  * Create a new Panel, that can contain a layout.Border.
37427  * 
37428  * 
37429  * @param {Roo.BorderLayout} layout The layout for this panel
37430  * @param {String/Object} config A string to set only the title or a config object
37431  */
37432 Roo.bootstrap.panel.Nest = function(config)
37433 {
37434     // construct with only one argument..
37435     /* FIXME - implement nicer consturctors
37436     if (layout.layout) {
37437         config = layout;
37438         layout = config.layout;
37439         delete config.layout;
37440     }
37441     if (layout.xtype && !layout.getEl) {
37442         // then layout needs constructing..
37443         layout = Roo.factory(layout, Roo);
37444     }
37445     */
37446     
37447     config.el =  config.layout.getEl();
37448     
37449     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37450     
37451     config.layout.monitorWindowResize = false; // turn off autosizing
37452     this.layout = config.layout;
37453     this.layout.getEl().addClass("roo-layout-nested-layout");
37454     
37455     
37456     
37457     
37458 };
37459
37460 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37461
37462     setSize : function(width, height){
37463         if(!this.ignoreResize(width, height)){
37464             var size = this.adjustForComponents(width, height);
37465             var el = this.layout.getEl();
37466             if (size.height < 1) {
37467                 el.setWidth(size.width);   
37468             } else {
37469                 el.setSize(size.width, size.height);
37470             }
37471             var touch = el.dom.offsetWidth;
37472             this.layout.layout();
37473             // ie requires a double layout on the first pass
37474             if(Roo.isIE && !this.initialized){
37475                 this.initialized = true;
37476                 this.layout.layout();
37477             }
37478         }
37479     },
37480     
37481     // activate all subpanels if not currently active..
37482     
37483     setActiveState : function(active){
37484         this.active = active;
37485         this.setActiveClass(active);
37486         
37487         if(!active){
37488             this.fireEvent("deactivate", this);
37489             return;
37490         }
37491         
37492         this.fireEvent("activate", this);
37493         // not sure if this should happen before or after..
37494         if (!this.layout) {
37495             return; // should not happen..
37496         }
37497         var reg = false;
37498         for (var r in this.layout.regions) {
37499             reg = this.layout.getRegion(r);
37500             if (reg.getActivePanel()) {
37501                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37502                 reg.setActivePanel(reg.getActivePanel());
37503                 continue;
37504             }
37505             if (!reg.panels.length) {
37506                 continue;
37507             }
37508             reg.showPanel(reg.getPanel(0));
37509         }
37510         
37511         
37512         
37513         
37514     },
37515     
37516     /**
37517      * Returns the nested BorderLayout for this panel
37518      * @return {Roo.BorderLayout} 
37519      */
37520     getLayout : function(){
37521         return this.layout;
37522     },
37523     
37524      /**
37525      * Adds a xtype elements to the layout of the nested panel
37526      * <pre><code>
37527
37528 panel.addxtype({
37529        xtype : 'ContentPanel',
37530        region: 'west',
37531        items: [ .... ]
37532    }
37533 );
37534
37535 panel.addxtype({
37536         xtype : 'NestedLayoutPanel',
37537         region: 'west',
37538         layout: {
37539            center: { },
37540            west: { }   
37541         },
37542         items : [ ... list of content panels or nested layout panels.. ]
37543    }
37544 );
37545 </code></pre>
37546      * @param {Object} cfg Xtype definition of item to add.
37547      */
37548     addxtype : function(cfg) {
37549         return this.layout.addxtype(cfg);
37550     
37551     }
37552 });        /*
37553  * Based on:
37554  * Ext JS Library 1.1.1
37555  * Copyright(c) 2006-2007, Ext JS, LLC.
37556  *
37557  * Originally Released Under LGPL - original licence link has changed is not relivant.
37558  *
37559  * Fork - LGPL
37560  * <script type="text/javascript">
37561  */
37562 /**
37563  * @class Roo.TabPanel
37564  * @extends Roo.util.Observable
37565  * A lightweight tab container.
37566  * <br><br>
37567  * Usage:
37568  * <pre><code>
37569 // basic tabs 1, built from existing content
37570 var tabs = new Roo.TabPanel("tabs1");
37571 tabs.addTab("script", "View Script");
37572 tabs.addTab("markup", "View Markup");
37573 tabs.activate("script");
37574
37575 // more advanced tabs, built from javascript
37576 var jtabs = new Roo.TabPanel("jtabs");
37577 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37578
37579 // set up the UpdateManager
37580 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37581 var updater = tab2.getUpdateManager();
37582 updater.setDefaultUrl("ajax1.htm");
37583 tab2.on('activate', updater.refresh, updater, true);
37584
37585 // Use setUrl for Ajax loading
37586 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37587 tab3.setUrl("ajax2.htm", null, true);
37588
37589 // Disabled tab
37590 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37591 tab4.disable();
37592
37593 jtabs.activate("jtabs-1");
37594  * </code></pre>
37595  * @constructor
37596  * Create a new TabPanel.
37597  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37598  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37599  */
37600 Roo.bootstrap.panel.Tabs = function(config){
37601     /**
37602     * The container element for this TabPanel.
37603     * @type Roo.Element
37604     */
37605     this.el = Roo.get(config.el);
37606     delete config.el;
37607     if(config){
37608         if(typeof config == "boolean"){
37609             this.tabPosition = config ? "bottom" : "top";
37610         }else{
37611             Roo.apply(this, config);
37612         }
37613     }
37614     
37615     if(this.tabPosition == "bottom"){
37616         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37617         this.el.addClass("roo-tabs-bottom");
37618     }
37619     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37620     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37621     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37622     if(Roo.isIE){
37623         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37624     }
37625     if(this.tabPosition != "bottom"){
37626         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37627          * @type Roo.Element
37628          */
37629         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37630         this.el.addClass("roo-tabs-top");
37631     }
37632     this.items = [];
37633
37634     this.bodyEl.setStyle("position", "relative");
37635
37636     this.active = null;
37637     this.activateDelegate = this.activate.createDelegate(this);
37638
37639     this.addEvents({
37640         /**
37641          * @event tabchange
37642          * Fires when the active tab changes
37643          * @param {Roo.TabPanel} this
37644          * @param {Roo.TabPanelItem} activePanel The new active tab
37645          */
37646         "tabchange": true,
37647         /**
37648          * @event beforetabchange
37649          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37650          * @param {Roo.TabPanel} this
37651          * @param {Object} e Set cancel to true on this object to cancel the tab change
37652          * @param {Roo.TabPanelItem} tab The tab being changed to
37653          */
37654         "beforetabchange" : true
37655     });
37656
37657     Roo.EventManager.onWindowResize(this.onResize, this);
37658     this.cpad = this.el.getPadding("lr");
37659     this.hiddenCount = 0;
37660
37661
37662     // toolbar on the tabbar support...
37663     if (this.toolbar) {
37664         alert("no toolbar support yet");
37665         this.toolbar  = false;
37666         /*
37667         var tcfg = this.toolbar;
37668         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37669         this.toolbar = new Roo.Toolbar(tcfg);
37670         if (Roo.isSafari) {
37671             var tbl = tcfg.container.child('table', true);
37672             tbl.setAttribute('width', '100%');
37673         }
37674         */
37675         
37676     }
37677    
37678
37679
37680     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37681 };
37682
37683 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37684     /*
37685      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37686      */
37687     tabPosition : "top",
37688     /*
37689      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37690      */
37691     currentTabWidth : 0,
37692     /*
37693      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37694      */
37695     minTabWidth : 40,
37696     /*
37697      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37698      */
37699     maxTabWidth : 250,
37700     /*
37701      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37702      */
37703     preferredTabWidth : 175,
37704     /*
37705      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37706      */
37707     resizeTabs : false,
37708     /*
37709      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37710      */
37711     monitorResize : true,
37712     /*
37713      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37714      */
37715     toolbar : false,
37716
37717     /**
37718      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37719      * @param {String} id The id of the div to use <b>or create</b>
37720      * @param {String} text The text for the tab
37721      * @param {String} content (optional) Content to put in the TabPanelItem body
37722      * @param {Boolean} closable (optional) True to create a close icon on the tab
37723      * @return {Roo.TabPanelItem} The created TabPanelItem
37724      */
37725     addTab : function(id, text, content, closable, tpl)
37726     {
37727         var item = new Roo.bootstrap.panel.TabItem({
37728             panel: this,
37729             id : id,
37730             text : text,
37731             closable : closable,
37732             tpl : tpl
37733         });
37734         this.addTabItem(item);
37735         if(content){
37736             item.setContent(content);
37737         }
37738         return item;
37739     },
37740
37741     /**
37742      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37743      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37744      * @return {Roo.TabPanelItem}
37745      */
37746     getTab : function(id){
37747         return this.items[id];
37748     },
37749
37750     /**
37751      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37752      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37753      */
37754     hideTab : function(id){
37755         var t = this.items[id];
37756         if(!t.isHidden()){
37757            t.setHidden(true);
37758            this.hiddenCount++;
37759            this.autoSizeTabs();
37760         }
37761     },
37762
37763     /**
37764      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37765      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37766      */
37767     unhideTab : function(id){
37768         var t = this.items[id];
37769         if(t.isHidden()){
37770            t.setHidden(false);
37771            this.hiddenCount--;
37772            this.autoSizeTabs();
37773         }
37774     },
37775
37776     /**
37777      * Adds an existing {@link Roo.TabPanelItem}.
37778      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37779      */
37780     addTabItem : function(item){
37781         this.items[item.id] = item;
37782         this.items.push(item);
37783       //  if(this.resizeTabs){
37784     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37785   //         this.autoSizeTabs();
37786 //        }else{
37787 //            item.autoSize();
37788        // }
37789     },
37790
37791     /**
37792      * Removes a {@link Roo.TabPanelItem}.
37793      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37794      */
37795     removeTab : function(id){
37796         var items = this.items;
37797         var tab = items[id];
37798         if(!tab) { return; }
37799         var index = items.indexOf(tab);
37800         if(this.active == tab && items.length > 1){
37801             var newTab = this.getNextAvailable(index);
37802             if(newTab) {
37803                 newTab.activate();
37804             }
37805         }
37806         this.stripEl.dom.removeChild(tab.pnode.dom);
37807         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37808             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37809         }
37810         items.splice(index, 1);
37811         delete this.items[tab.id];
37812         tab.fireEvent("close", tab);
37813         tab.purgeListeners();
37814         this.autoSizeTabs();
37815     },
37816
37817     getNextAvailable : function(start){
37818         var items = this.items;
37819         var index = start;
37820         // look for a next tab that will slide over to
37821         // replace the one being removed
37822         while(index < items.length){
37823             var item = items[++index];
37824             if(item && !item.isHidden()){
37825                 return item;
37826             }
37827         }
37828         // if one isn't found select the previous tab (on the left)
37829         index = start;
37830         while(index >= 0){
37831             var item = items[--index];
37832             if(item && !item.isHidden()){
37833                 return item;
37834             }
37835         }
37836         return null;
37837     },
37838
37839     /**
37840      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37841      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37842      */
37843     disableTab : function(id){
37844         var tab = this.items[id];
37845         if(tab && this.active != tab){
37846             tab.disable();
37847         }
37848     },
37849
37850     /**
37851      * Enables a {@link Roo.TabPanelItem} that is disabled.
37852      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37853      */
37854     enableTab : function(id){
37855         var tab = this.items[id];
37856         tab.enable();
37857     },
37858
37859     /**
37860      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37861      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37862      * @return {Roo.TabPanelItem} The TabPanelItem.
37863      */
37864     activate : function(id){
37865         var tab = this.items[id];
37866         if(!tab){
37867             return null;
37868         }
37869         if(tab == this.active || tab.disabled){
37870             return tab;
37871         }
37872         var e = {};
37873         this.fireEvent("beforetabchange", this, e, tab);
37874         if(e.cancel !== true && !tab.disabled){
37875             if(this.active){
37876                 this.active.hide();
37877             }
37878             this.active = this.items[id];
37879             this.active.show();
37880             this.fireEvent("tabchange", this, this.active);
37881         }
37882         return tab;
37883     },
37884
37885     /**
37886      * Gets the active {@link Roo.TabPanelItem}.
37887      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37888      */
37889     getActiveTab : function(){
37890         return this.active;
37891     },
37892
37893     /**
37894      * Updates the tab body element to fit the height of the container element
37895      * for overflow scrolling
37896      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37897      */
37898     syncHeight : function(targetHeight){
37899         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37900         var bm = this.bodyEl.getMargins();
37901         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37902         this.bodyEl.setHeight(newHeight);
37903         return newHeight;
37904     },
37905
37906     onResize : function(){
37907         if(this.monitorResize){
37908             this.autoSizeTabs();
37909         }
37910     },
37911
37912     /**
37913      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37914      */
37915     beginUpdate : function(){
37916         this.updating = true;
37917     },
37918
37919     /**
37920      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37921      */
37922     endUpdate : function(){
37923         this.updating = false;
37924         this.autoSizeTabs();
37925     },
37926
37927     /**
37928      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37929      */
37930     autoSizeTabs : function(){
37931         var count = this.items.length;
37932         var vcount = count - this.hiddenCount;
37933         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37934             return;
37935         }
37936         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37937         var availWidth = Math.floor(w / vcount);
37938         var b = this.stripBody;
37939         if(b.getWidth() > w){
37940             var tabs = this.items;
37941             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37942             if(availWidth < this.minTabWidth){
37943                 /*if(!this.sleft){    // incomplete scrolling code
37944                     this.createScrollButtons();
37945                 }
37946                 this.showScroll();
37947                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37948             }
37949         }else{
37950             if(this.currentTabWidth < this.preferredTabWidth){
37951                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37952             }
37953         }
37954     },
37955
37956     /**
37957      * Returns the number of tabs in this TabPanel.
37958      * @return {Number}
37959      */
37960      getCount : function(){
37961          return this.items.length;
37962      },
37963
37964     /**
37965      * Resizes all the tabs to the passed width
37966      * @param {Number} The new width
37967      */
37968     setTabWidth : function(width){
37969         this.currentTabWidth = width;
37970         for(var i = 0, len = this.items.length; i < len; i++) {
37971                 if(!this.items[i].isHidden()) {
37972                 this.items[i].setWidth(width);
37973             }
37974         }
37975     },
37976
37977     /**
37978      * Destroys this TabPanel
37979      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37980      */
37981     destroy : function(removeEl){
37982         Roo.EventManager.removeResizeListener(this.onResize, this);
37983         for(var i = 0, len = this.items.length; i < len; i++){
37984             this.items[i].purgeListeners();
37985         }
37986         if(removeEl === true){
37987             this.el.update("");
37988             this.el.remove();
37989         }
37990     },
37991     
37992     createStrip : function(container)
37993     {
37994         var strip = document.createElement("nav");
37995         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37996         container.appendChild(strip);
37997         return strip;
37998     },
37999     
38000     createStripList : function(strip)
38001     {
38002         // div wrapper for retard IE
38003         // returns the "tr" element.
38004         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38005         //'<div class="x-tabs-strip-wrap">'+
38006           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38007           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38008         return strip.firstChild; //.firstChild.firstChild.firstChild;
38009     },
38010     createBody : function(container)
38011     {
38012         var body = document.createElement("div");
38013         Roo.id(body, "tab-body");
38014         //Roo.fly(body).addClass("x-tabs-body");
38015         Roo.fly(body).addClass("tab-content");
38016         container.appendChild(body);
38017         return body;
38018     },
38019     createItemBody :function(bodyEl, id){
38020         var body = Roo.getDom(id);
38021         if(!body){
38022             body = document.createElement("div");
38023             body.id = id;
38024         }
38025         //Roo.fly(body).addClass("x-tabs-item-body");
38026         Roo.fly(body).addClass("tab-pane");
38027          bodyEl.insertBefore(body, bodyEl.firstChild);
38028         return body;
38029     },
38030     /** @private */
38031     createStripElements :  function(stripEl, text, closable, tpl)
38032     {
38033         var td = document.createElement("li"); // was td..
38034         
38035         
38036         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38037         
38038         
38039         stripEl.appendChild(td);
38040         /*if(closable){
38041             td.className = "x-tabs-closable";
38042             if(!this.closeTpl){
38043                 this.closeTpl = new Roo.Template(
38044                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38045                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38046                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38047                 );
38048             }
38049             var el = this.closeTpl.overwrite(td, {"text": text});
38050             var close = el.getElementsByTagName("div")[0];
38051             var inner = el.getElementsByTagName("em")[0];
38052             return {"el": el, "close": close, "inner": inner};
38053         } else {
38054         */
38055         // not sure what this is..
38056 //            if(!this.tabTpl){
38057                 //this.tabTpl = new Roo.Template(
38058                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38059                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38060                 //);
38061 //                this.tabTpl = new Roo.Template(
38062 //                   '<a href="#">' +
38063 //                   '<span unselectable="on"' +
38064 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38065 //                            ' >{text}</span></a>'
38066 //                );
38067 //                
38068 //            }
38069
38070
38071             var template = tpl || this.tabTpl || false;
38072             
38073             if(!template){
38074                 
38075                 template = new Roo.Template(
38076                    '<a href="#">' +
38077                    '<span unselectable="on"' +
38078                             (this.disableTooltips ? '' : ' title="{text}"') +
38079                             ' >{text}</span></a>'
38080                 );
38081             }
38082             
38083             switch (typeof(template)) {
38084                 case 'object' :
38085                     break;
38086                 case 'string' :
38087                     template = new Roo.Template(template);
38088                     break;
38089                 default :
38090                     break;
38091             }
38092             
38093             var el = template.overwrite(td, {"text": text});
38094             
38095             var inner = el.getElementsByTagName("span")[0];
38096             
38097             return {"el": el, "inner": inner};
38098             
38099     }
38100         
38101     
38102 });
38103
38104 /**
38105  * @class Roo.TabPanelItem
38106  * @extends Roo.util.Observable
38107  * Represents an individual item (tab plus body) in a TabPanel.
38108  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38109  * @param {String} id The id of this TabPanelItem
38110  * @param {String} text The text for the tab of this TabPanelItem
38111  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38112  */
38113 Roo.bootstrap.panel.TabItem = function(config){
38114     /**
38115      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38116      * @type Roo.TabPanel
38117      */
38118     this.tabPanel = config.panel;
38119     /**
38120      * The id for this TabPanelItem
38121      * @type String
38122      */
38123     this.id = config.id;
38124     /** @private */
38125     this.disabled = false;
38126     /** @private */
38127     this.text = config.text;
38128     /** @private */
38129     this.loaded = false;
38130     this.closable = config.closable;
38131
38132     /**
38133      * The body element for this TabPanelItem.
38134      * @type Roo.Element
38135      */
38136     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38137     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38138     this.bodyEl.setStyle("display", "block");
38139     this.bodyEl.setStyle("zoom", "1");
38140     //this.hideAction();
38141
38142     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38143     /** @private */
38144     this.el = Roo.get(els.el);
38145     this.inner = Roo.get(els.inner, true);
38146     this.textEl = Roo.get(this.el.dom.firstChild, true);
38147     this.pnode = Roo.get(els.el.parentNode, true);
38148 //    this.el.on("mousedown", this.onTabMouseDown, this);
38149     this.el.on("click", this.onTabClick, this);
38150     /** @private */
38151     if(config.closable){
38152         var c = Roo.get(els.close, true);
38153         c.dom.title = this.closeText;
38154         c.addClassOnOver("close-over");
38155         c.on("click", this.closeClick, this);
38156      }
38157
38158     this.addEvents({
38159          /**
38160          * @event activate
38161          * Fires when this tab becomes the active tab.
38162          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38163          * @param {Roo.TabPanelItem} this
38164          */
38165         "activate": true,
38166         /**
38167          * @event beforeclose
38168          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38169          * @param {Roo.TabPanelItem} this
38170          * @param {Object} e Set cancel to true on this object to cancel the close.
38171          */
38172         "beforeclose": true,
38173         /**
38174          * @event close
38175          * Fires when this tab is closed.
38176          * @param {Roo.TabPanelItem} this
38177          */
38178          "close": true,
38179         /**
38180          * @event deactivate
38181          * Fires when this tab is no longer the active tab.
38182          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38183          * @param {Roo.TabPanelItem} this
38184          */
38185          "deactivate" : true
38186     });
38187     this.hidden = false;
38188
38189     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38190 };
38191
38192 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38193            {
38194     purgeListeners : function(){
38195        Roo.util.Observable.prototype.purgeListeners.call(this);
38196        this.el.removeAllListeners();
38197     },
38198     /**
38199      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38200      */
38201     show : function(){
38202         this.pnode.addClass("active");
38203         this.showAction();
38204         if(Roo.isOpera){
38205             this.tabPanel.stripWrap.repaint();
38206         }
38207         this.fireEvent("activate", this.tabPanel, this);
38208     },
38209
38210     /**
38211      * Returns true if this tab is the active tab.
38212      * @return {Boolean}
38213      */
38214     isActive : function(){
38215         return this.tabPanel.getActiveTab() == this;
38216     },
38217
38218     /**
38219      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38220      */
38221     hide : function(){
38222         this.pnode.removeClass("active");
38223         this.hideAction();
38224         this.fireEvent("deactivate", this.tabPanel, this);
38225     },
38226
38227     hideAction : function(){
38228         this.bodyEl.hide();
38229         this.bodyEl.setStyle("position", "absolute");
38230         this.bodyEl.setLeft("-20000px");
38231         this.bodyEl.setTop("-20000px");
38232     },
38233
38234     showAction : function(){
38235         this.bodyEl.setStyle("position", "relative");
38236         this.bodyEl.setTop("");
38237         this.bodyEl.setLeft("");
38238         this.bodyEl.show();
38239     },
38240
38241     /**
38242      * Set the tooltip for the tab.
38243      * @param {String} tooltip The tab's tooltip
38244      */
38245     setTooltip : function(text){
38246         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38247             this.textEl.dom.qtip = text;
38248             this.textEl.dom.removeAttribute('title');
38249         }else{
38250             this.textEl.dom.title = text;
38251         }
38252     },
38253
38254     onTabClick : function(e){
38255         e.preventDefault();
38256         this.tabPanel.activate(this.id);
38257     },
38258
38259     onTabMouseDown : function(e){
38260         e.preventDefault();
38261         this.tabPanel.activate(this.id);
38262     },
38263 /*
38264     getWidth : function(){
38265         return this.inner.getWidth();
38266     },
38267
38268     setWidth : function(width){
38269         var iwidth = width - this.pnode.getPadding("lr");
38270         this.inner.setWidth(iwidth);
38271         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38272         this.pnode.setWidth(width);
38273     },
38274 */
38275     /**
38276      * Show or hide the tab
38277      * @param {Boolean} hidden True to hide or false to show.
38278      */
38279     setHidden : function(hidden){
38280         this.hidden = hidden;
38281         this.pnode.setStyle("display", hidden ? "none" : "");
38282     },
38283
38284     /**
38285      * Returns true if this tab is "hidden"
38286      * @return {Boolean}
38287      */
38288     isHidden : function(){
38289         return this.hidden;
38290     },
38291
38292     /**
38293      * Returns the text for this tab
38294      * @return {String}
38295      */
38296     getText : function(){
38297         return this.text;
38298     },
38299     /*
38300     autoSize : function(){
38301         //this.el.beginMeasure();
38302         this.textEl.setWidth(1);
38303         /*
38304          *  #2804 [new] Tabs in Roojs
38305          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38306          */
38307         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38308         //this.el.endMeasure();
38309     //},
38310
38311     /**
38312      * Sets the text for the tab (Note: this also sets the tooltip text)
38313      * @param {String} text The tab's text and tooltip
38314      */
38315     setText : function(text){
38316         this.text = text;
38317         this.textEl.update(text);
38318         this.setTooltip(text);
38319         //if(!this.tabPanel.resizeTabs){
38320         //    this.autoSize();
38321         //}
38322     },
38323     /**
38324      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38325      */
38326     activate : function(){
38327         this.tabPanel.activate(this.id);
38328     },
38329
38330     /**
38331      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38332      */
38333     disable : function(){
38334         if(this.tabPanel.active != this){
38335             this.disabled = true;
38336             this.pnode.addClass("disabled");
38337         }
38338     },
38339
38340     /**
38341      * Enables this TabPanelItem if it was previously disabled.
38342      */
38343     enable : function(){
38344         this.disabled = false;
38345         this.pnode.removeClass("disabled");
38346     },
38347
38348     /**
38349      * Sets the content for this TabPanelItem.
38350      * @param {String} content The content
38351      * @param {Boolean} loadScripts true to look for and load scripts
38352      */
38353     setContent : function(content, loadScripts){
38354         this.bodyEl.update(content, loadScripts);
38355     },
38356
38357     /**
38358      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38359      * @return {Roo.UpdateManager} The UpdateManager
38360      */
38361     getUpdateManager : function(){
38362         return this.bodyEl.getUpdateManager();
38363     },
38364
38365     /**
38366      * Set a URL to be used to load the content for this TabPanelItem.
38367      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38368      * @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)
38369      * @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)
38370      * @return {Roo.UpdateManager} The UpdateManager
38371      */
38372     setUrl : function(url, params, loadOnce){
38373         if(this.refreshDelegate){
38374             this.un('activate', this.refreshDelegate);
38375         }
38376         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38377         this.on("activate", this.refreshDelegate);
38378         return this.bodyEl.getUpdateManager();
38379     },
38380
38381     /** @private */
38382     _handleRefresh : function(url, params, loadOnce){
38383         if(!loadOnce || !this.loaded){
38384             var updater = this.bodyEl.getUpdateManager();
38385             updater.update(url, params, this._setLoaded.createDelegate(this));
38386         }
38387     },
38388
38389     /**
38390      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38391      *   Will fail silently if the setUrl method has not been called.
38392      *   This does not activate the panel, just updates its content.
38393      */
38394     refresh : function(){
38395         if(this.refreshDelegate){
38396            this.loaded = false;
38397            this.refreshDelegate();
38398         }
38399     },
38400
38401     /** @private */
38402     _setLoaded : function(){
38403         this.loaded = true;
38404     },
38405
38406     /** @private */
38407     closeClick : function(e){
38408         var o = {};
38409         e.stopEvent();
38410         this.fireEvent("beforeclose", this, o);
38411         if(o.cancel !== true){
38412             this.tabPanel.removeTab(this.id);
38413         }
38414     },
38415     /**
38416      * The text displayed in the tooltip for the close icon.
38417      * @type String
38418      */
38419     closeText : "Close this tab"
38420 });
38421 /**
38422 *    This script refer to:
38423 *    Title: International Telephone Input
38424 *    Author: Jack O'Connor
38425 *    Code version:  v12.1.12
38426 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38427 **/
38428
38429 Roo.bootstrap.PhoneInputData = function() {
38430     var d = [
38431       [
38432         "Afghanistan (‫افغانستان‬‎)",
38433         "af",
38434         "93"
38435       ],
38436       [
38437         "Albania (Shqipëri)",
38438         "al",
38439         "355"
38440       ],
38441       [
38442         "Algeria (‫الجزائر‬‎)",
38443         "dz",
38444         "213"
38445       ],
38446       [
38447         "American Samoa",
38448         "as",
38449         "1684"
38450       ],
38451       [
38452         "Andorra",
38453         "ad",
38454         "376"
38455       ],
38456       [
38457         "Angola",
38458         "ao",
38459         "244"
38460       ],
38461       [
38462         "Anguilla",
38463         "ai",
38464         "1264"
38465       ],
38466       [
38467         "Antigua and Barbuda",
38468         "ag",
38469         "1268"
38470       ],
38471       [
38472         "Argentina",
38473         "ar",
38474         "54"
38475       ],
38476       [
38477         "Armenia (Հայաստան)",
38478         "am",
38479         "374"
38480       ],
38481       [
38482         "Aruba",
38483         "aw",
38484         "297"
38485       ],
38486       [
38487         "Australia",
38488         "au",
38489         "61",
38490         0
38491       ],
38492       [
38493         "Austria (Österreich)",
38494         "at",
38495         "43"
38496       ],
38497       [
38498         "Azerbaijan (Azərbaycan)",
38499         "az",
38500         "994"
38501       ],
38502       [
38503         "Bahamas",
38504         "bs",
38505         "1242"
38506       ],
38507       [
38508         "Bahrain (‫البحرين‬‎)",
38509         "bh",
38510         "973"
38511       ],
38512       [
38513         "Bangladesh (বাংলাদেশ)",
38514         "bd",
38515         "880"
38516       ],
38517       [
38518         "Barbados",
38519         "bb",
38520         "1246"
38521       ],
38522       [
38523         "Belarus (Беларусь)",
38524         "by",
38525         "375"
38526       ],
38527       [
38528         "Belgium (België)",
38529         "be",
38530         "32"
38531       ],
38532       [
38533         "Belize",
38534         "bz",
38535         "501"
38536       ],
38537       [
38538         "Benin (Bénin)",
38539         "bj",
38540         "229"
38541       ],
38542       [
38543         "Bermuda",
38544         "bm",
38545         "1441"
38546       ],
38547       [
38548         "Bhutan (འབྲུག)",
38549         "bt",
38550         "975"
38551       ],
38552       [
38553         "Bolivia",
38554         "bo",
38555         "591"
38556       ],
38557       [
38558         "Bosnia and Herzegovina (Босна и Херцеговина)",
38559         "ba",
38560         "387"
38561       ],
38562       [
38563         "Botswana",
38564         "bw",
38565         "267"
38566       ],
38567       [
38568         "Brazil (Brasil)",
38569         "br",
38570         "55"
38571       ],
38572       [
38573         "British Indian Ocean Territory",
38574         "io",
38575         "246"
38576       ],
38577       [
38578         "British Virgin Islands",
38579         "vg",
38580         "1284"
38581       ],
38582       [
38583         "Brunei",
38584         "bn",
38585         "673"
38586       ],
38587       [
38588         "Bulgaria (България)",
38589         "bg",
38590         "359"
38591       ],
38592       [
38593         "Burkina Faso",
38594         "bf",
38595         "226"
38596       ],
38597       [
38598         "Burundi (Uburundi)",
38599         "bi",
38600         "257"
38601       ],
38602       [
38603         "Cambodia (កម្ពុជា)",
38604         "kh",
38605         "855"
38606       ],
38607       [
38608         "Cameroon (Cameroun)",
38609         "cm",
38610         "237"
38611       ],
38612       [
38613         "Canada",
38614         "ca",
38615         "1",
38616         1,
38617         ["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"]
38618       ],
38619       [
38620         "Cape Verde (Kabu Verdi)",
38621         "cv",
38622         "238"
38623       ],
38624       [
38625         "Caribbean Netherlands",
38626         "bq",
38627         "599",
38628         1
38629       ],
38630       [
38631         "Cayman Islands",
38632         "ky",
38633         "1345"
38634       ],
38635       [
38636         "Central African Republic (République centrafricaine)",
38637         "cf",
38638         "236"
38639       ],
38640       [
38641         "Chad (Tchad)",
38642         "td",
38643         "235"
38644       ],
38645       [
38646         "Chile",
38647         "cl",
38648         "56"
38649       ],
38650       [
38651         "China (中国)",
38652         "cn",
38653         "86"
38654       ],
38655       [
38656         "Christmas Island",
38657         "cx",
38658         "61",
38659         2
38660       ],
38661       [
38662         "Cocos (Keeling) Islands",
38663         "cc",
38664         "61",
38665         1
38666       ],
38667       [
38668         "Colombia",
38669         "co",
38670         "57"
38671       ],
38672       [
38673         "Comoros (‫جزر القمر‬‎)",
38674         "km",
38675         "269"
38676       ],
38677       [
38678         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38679         "cd",
38680         "243"
38681       ],
38682       [
38683         "Congo (Republic) (Congo-Brazzaville)",
38684         "cg",
38685         "242"
38686       ],
38687       [
38688         "Cook Islands",
38689         "ck",
38690         "682"
38691       ],
38692       [
38693         "Costa Rica",
38694         "cr",
38695         "506"
38696       ],
38697       [
38698         "Côte d’Ivoire",
38699         "ci",
38700         "225"
38701       ],
38702       [
38703         "Croatia (Hrvatska)",
38704         "hr",
38705         "385"
38706       ],
38707       [
38708         "Cuba",
38709         "cu",
38710         "53"
38711       ],
38712       [
38713         "Curaçao",
38714         "cw",
38715         "599",
38716         0
38717       ],
38718       [
38719         "Cyprus (Κύπρος)",
38720         "cy",
38721         "357"
38722       ],
38723       [
38724         "Czech Republic (Česká republika)",
38725         "cz",
38726         "420"
38727       ],
38728       [
38729         "Denmark (Danmark)",
38730         "dk",
38731         "45"
38732       ],
38733       [
38734         "Djibouti",
38735         "dj",
38736         "253"
38737       ],
38738       [
38739         "Dominica",
38740         "dm",
38741         "1767"
38742       ],
38743       [
38744         "Dominican Republic (República Dominicana)",
38745         "do",
38746         "1",
38747         2,
38748         ["809", "829", "849"]
38749       ],
38750       [
38751         "Ecuador",
38752         "ec",
38753         "593"
38754       ],
38755       [
38756         "Egypt (‫مصر‬‎)",
38757         "eg",
38758         "20"
38759       ],
38760       [
38761         "El Salvador",
38762         "sv",
38763         "503"
38764       ],
38765       [
38766         "Equatorial Guinea (Guinea Ecuatorial)",
38767         "gq",
38768         "240"
38769       ],
38770       [
38771         "Eritrea",
38772         "er",
38773         "291"
38774       ],
38775       [
38776         "Estonia (Eesti)",
38777         "ee",
38778         "372"
38779       ],
38780       [
38781         "Ethiopia",
38782         "et",
38783         "251"
38784       ],
38785       [
38786         "Falkland Islands (Islas Malvinas)",
38787         "fk",
38788         "500"
38789       ],
38790       [
38791         "Faroe Islands (Føroyar)",
38792         "fo",
38793         "298"
38794       ],
38795       [
38796         "Fiji",
38797         "fj",
38798         "679"
38799       ],
38800       [
38801         "Finland (Suomi)",
38802         "fi",
38803         "358",
38804         0
38805       ],
38806       [
38807         "France",
38808         "fr",
38809         "33"
38810       ],
38811       [
38812         "French Guiana (Guyane française)",
38813         "gf",
38814         "594"
38815       ],
38816       [
38817         "French Polynesia (Polynésie française)",
38818         "pf",
38819         "689"
38820       ],
38821       [
38822         "Gabon",
38823         "ga",
38824         "241"
38825       ],
38826       [
38827         "Gambia",
38828         "gm",
38829         "220"
38830       ],
38831       [
38832         "Georgia (საქართველო)",
38833         "ge",
38834         "995"
38835       ],
38836       [
38837         "Germany (Deutschland)",
38838         "de",
38839         "49"
38840       ],
38841       [
38842         "Ghana (Gaana)",
38843         "gh",
38844         "233"
38845       ],
38846       [
38847         "Gibraltar",
38848         "gi",
38849         "350"
38850       ],
38851       [
38852         "Greece (Ελλάδα)",
38853         "gr",
38854         "30"
38855       ],
38856       [
38857         "Greenland (Kalaallit Nunaat)",
38858         "gl",
38859         "299"
38860       ],
38861       [
38862         "Grenada",
38863         "gd",
38864         "1473"
38865       ],
38866       [
38867         "Guadeloupe",
38868         "gp",
38869         "590",
38870         0
38871       ],
38872       [
38873         "Guam",
38874         "gu",
38875         "1671"
38876       ],
38877       [
38878         "Guatemala",
38879         "gt",
38880         "502"
38881       ],
38882       [
38883         "Guernsey",
38884         "gg",
38885         "44",
38886         1
38887       ],
38888       [
38889         "Guinea (Guinée)",
38890         "gn",
38891         "224"
38892       ],
38893       [
38894         "Guinea-Bissau (Guiné Bissau)",
38895         "gw",
38896         "245"
38897       ],
38898       [
38899         "Guyana",
38900         "gy",
38901         "592"
38902       ],
38903       [
38904         "Haiti",
38905         "ht",
38906         "509"
38907       ],
38908       [
38909         "Honduras",
38910         "hn",
38911         "504"
38912       ],
38913       [
38914         "Hong Kong (香港)",
38915         "hk",
38916         "852"
38917       ],
38918       [
38919         "Hungary (Magyarország)",
38920         "hu",
38921         "36"
38922       ],
38923       [
38924         "Iceland (Ísland)",
38925         "is",
38926         "354"
38927       ],
38928       [
38929         "India (भारत)",
38930         "in",
38931         "91"
38932       ],
38933       [
38934         "Indonesia",
38935         "id",
38936         "62"
38937       ],
38938       [
38939         "Iran (‫ایران‬‎)",
38940         "ir",
38941         "98"
38942       ],
38943       [
38944         "Iraq (‫العراق‬‎)",
38945         "iq",
38946         "964"
38947       ],
38948       [
38949         "Ireland",
38950         "ie",
38951         "353"
38952       ],
38953       [
38954         "Isle of Man",
38955         "im",
38956         "44",
38957         2
38958       ],
38959       [
38960         "Israel (‫ישראל‬‎)",
38961         "il",
38962         "972"
38963       ],
38964       [
38965         "Italy (Italia)",
38966         "it",
38967         "39",
38968         0
38969       ],
38970       [
38971         "Jamaica",
38972         "jm",
38973         "1876"
38974       ],
38975       [
38976         "Japan (日本)",
38977         "jp",
38978         "81"
38979       ],
38980       [
38981         "Jersey",
38982         "je",
38983         "44",
38984         3
38985       ],
38986       [
38987         "Jordan (‫الأردن‬‎)",
38988         "jo",
38989         "962"
38990       ],
38991       [
38992         "Kazakhstan (Казахстан)",
38993         "kz",
38994         "7",
38995         1
38996       ],
38997       [
38998         "Kenya",
38999         "ke",
39000         "254"
39001       ],
39002       [
39003         "Kiribati",
39004         "ki",
39005         "686"
39006       ],
39007       [
39008         "Kosovo",
39009         "xk",
39010         "383"
39011       ],
39012       [
39013         "Kuwait (‫الكويت‬‎)",
39014         "kw",
39015         "965"
39016       ],
39017       [
39018         "Kyrgyzstan (Кыргызстан)",
39019         "kg",
39020         "996"
39021       ],
39022       [
39023         "Laos (ລາວ)",
39024         "la",
39025         "856"
39026       ],
39027       [
39028         "Latvia (Latvija)",
39029         "lv",
39030         "371"
39031       ],
39032       [
39033         "Lebanon (‫لبنان‬‎)",
39034         "lb",
39035         "961"
39036       ],
39037       [
39038         "Lesotho",
39039         "ls",
39040         "266"
39041       ],
39042       [
39043         "Liberia",
39044         "lr",
39045         "231"
39046       ],
39047       [
39048         "Libya (‫ليبيا‬‎)",
39049         "ly",
39050         "218"
39051       ],
39052       [
39053         "Liechtenstein",
39054         "li",
39055         "423"
39056       ],
39057       [
39058         "Lithuania (Lietuva)",
39059         "lt",
39060         "370"
39061       ],
39062       [
39063         "Luxembourg",
39064         "lu",
39065         "352"
39066       ],
39067       [
39068         "Macau (澳門)",
39069         "mo",
39070         "853"
39071       ],
39072       [
39073         "Macedonia (FYROM) (Македонија)",
39074         "mk",
39075         "389"
39076       ],
39077       [
39078         "Madagascar (Madagasikara)",
39079         "mg",
39080         "261"
39081       ],
39082       [
39083         "Malawi",
39084         "mw",
39085         "265"
39086       ],
39087       [
39088         "Malaysia",
39089         "my",
39090         "60"
39091       ],
39092       [
39093         "Maldives",
39094         "mv",
39095         "960"
39096       ],
39097       [
39098         "Mali",
39099         "ml",
39100         "223"
39101       ],
39102       [
39103         "Malta",
39104         "mt",
39105         "356"
39106       ],
39107       [
39108         "Marshall Islands",
39109         "mh",
39110         "692"
39111       ],
39112       [
39113         "Martinique",
39114         "mq",
39115         "596"
39116       ],
39117       [
39118         "Mauritania (‫موريتانيا‬‎)",
39119         "mr",
39120         "222"
39121       ],
39122       [
39123         "Mauritius (Moris)",
39124         "mu",
39125         "230"
39126       ],
39127       [
39128         "Mayotte",
39129         "yt",
39130         "262",
39131         1
39132       ],
39133       [
39134         "Mexico (México)",
39135         "mx",
39136         "52"
39137       ],
39138       [
39139         "Micronesia",
39140         "fm",
39141         "691"
39142       ],
39143       [
39144         "Moldova (Republica Moldova)",
39145         "md",
39146         "373"
39147       ],
39148       [
39149         "Monaco",
39150         "mc",
39151         "377"
39152       ],
39153       [
39154         "Mongolia (Монгол)",
39155         "mn",
39156         "976"
39157       ],
39158       [
39159         "Montenegro (Crna Gora)",
39160         "me",
39161         "382"
39162       ],
39163       [
39164         "Montserrat",
39165         "ms",
39166         "1664"
39167       ],
39168       [
39169         "Morocco (‫المغرب‬‎)",
39170         "ma",
39171         "212",
39172         0
39173       ],
39174       [
39175         "Mozambique (Moçambique)",
39176         "mz",
39177         "258"
39178       ],
39179       [
39180         "Myanmar (Burma) (မြန်မာ)",
39181         "mm",
39182         "95"
39183       ],
39184       [
39185         "Namibia (Namibië)",
39186         "na",
39187         "264"
39188       ],
39189       [
39190         "Nauru",
39191         "nr",
39192         "674"
39193       ],
39194       [
39195         "Nepal (नेपाल)",
39196         "np",
39197         "977"
39198       ],
39199       [
39200         "Netherlands (Nederland)",
39201         "nl",
39202         "31"
39203       ],
39204       [
39205         "New Caledonia (Nouvelle-Calédonie)",
39206         "nc",
39207         "687"
39208       ],
39209       [
39210         "New Zealand",
39211         "nz",
39212         "64"
39213       ],
39214       [
39215         "Nicaragua",
39216         "ni",
39217         "505"
39218       ],
39219       [
39220         "Niger (Nijar)",
39221         "ne",
39222         "227"
39223       ],
39224       [
39225         "Nigeria",
39226         "ng",
39227         "234"
39228       ],
39229       [
39230         "Niue",
39231         "nu",
39232         "683"
39233       ],
39234       [
39235         "Norfolk Island",
39236         "nf",
39237         "672"
39238       ],
39239       [
39240         "North Korea (조선 민주주의 인민 공화국)",
39241         "kp",
39242         "850"
39243       ],
39244       [
39245         "Northern Mariana Islands",
39246         "mp",
39247         "1670"
39248       ],
39249       [
39250         "Norway (Norge)",
39251         "no",
39252         "47",
39253         0
39254       ],
39255       [
39256         "Oman (‫عُمان‬‎)",
39257         "om",
39258         "968"
39259       ],
39260       [
39261         "Pakistan (‫پاکستان‬‎)",
39262         "pk",
39263         "92"
39264       ],
39265       [
39266         "Palau",
39267         "pw",
39268         "680"
39269       ],
39270       [
39271         "Palestine (‫فلسطين‬‎)",
39272         "ps",
39273         "970"
39274       ],
39275       [
39276         "Panama (Panamá)",
39277         "pa",
39278         "507"
39279       ],
39280       [
39281         "Papua New Guinea",
39282         "pg",
39283         "675"
39284       ],
39285       [
39286         "Paraguay",
39287         "py",
39288         "595"
39289       ],
39290       [
39291         "Peru (Perú)",
39292         "pe",
39293         "51"
39294       ],
39295       [
39296         "Philippines",
39297         "ph",
39298         "63"
39299       ],
39300       [
39301         "Poland (Polska)",
39302         "pl",
39303         "48"
39304       ],
39305       [
39306         "Portugal",
39307         "pt",
39308         "351"
39309       ],
39310       [
39311         "Puerto Rico",
39312         "pr",
39313         "1",
39314         3,
39315         ["787", "939"]
39316       ],
39317       [
39318         "Qatar (‫قطر‬‎)",
39319         "qa",
39320         "974"
39321       ],
39322       [
39323         "Réunion (La Réunion)",
39324         "re",
39325         "262",
39326         0
39327       ],
39328       [
39329         "Romania (România)",
39330         "ro",
39331         "40"
39332       ],
39333       [
39334         "Russia (Россия)",
39335         "ru",
39336         "7",
39337         0
39338       ],
39339       [
39340         "Rwanda",
39341         "rw",
39342         "250"
39343       ],
39344       [
39345         "Saint Barthélemy",
39346         "bl",
39347         "590",
39348         1
39349       ],
39350       [
39351         "Saint Helena",
39352         "sh",
39353         "290"
39354       ],
39355       [
39356         "Saint Kitts and Nevis",
39357         "kn",
39358         "1869"
39359       ],
39360       [
39361         "Saint Lucia",
39362         "lc",
39363         "1758"
39364       ],
39365       [
39366         "Saint Martin (Saint-Martin (partie française))",
39367         "mf",
39368         "590",
39369         2
39370       ],
39371       [
39372         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39373         "pm",
39374         "508"
39375       ],
39376       [
39377         "Saint Vincent and the Grenadines",
39378         "vc",
39379         "1784"
39380       ],
39381       [
39382         "Samoa",
39383         "ws",
39384         "685"
39385       ],
39386       [
39387         "San Marino",
39388         "sm",
39389         "378"
39390       ],
39391       [
39392         "São Tomé and Príncipe (São Tomé e Príncipe)",
39393         "st",
39394         "239"
39395       ],
39396       [
39397         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39398         "sa",
39399         "966"
39400       ],
39401       [
39402         "Senegal (Sénégal)",
39403         "sn",
39404         "221"
39405       ],
39406       [
39407         "Serbia (Србија)",
39408         "rs",
39409         "381"
39410       ],
39411       [
39412         "Seychelles",
39413         "sc",
39414         "248"
39415       ],
39416       [
39417         "Sierra Leone",
39418         "sl",
39419         "232"
39420       ],
39421       [
39422         "Singapore",
39423         "sg",
39424         "65"
39425       ],
39426       [
39427         "Sint Maarten",
39428         "sx",
39429         "1721"
39430       ],
39431       [
39432         "Slovakia (Slovensko)",
39433         "sk",
39434         "421"
39435       ],
39436       [
39437         "Slovenia (Slovenija)",
39438         "si",
39439         "386"
39440       ],
39441       [
39442         "Solomon Islands",
39443         "sb",
39444         "677"
39445       ],
39446       [
39447         "Somalia (Soomaaliya)",
39448         "so",
39449         "252"
39450       ],
39451       [
39452         "South Africa",
39453         "za",
39454         "27"
39455       ],
39456       [
39457         "South Korea (대한민국)",
39458         "kr",
39459         "82"
39460       ],
39461       [
39462         "South Sudan (‫جنوب السودان‬‎)",
39463         "ss",
39464         "211"
39465       ],
39466       [
39467         "Spain (España)",
39468         "es",
39469         "34"
39470       ],
39471       [
39472         "Sri Lanka (ශ්‍රී ලංකාව)",
39473         "lk",
39474         "94"
39475       ],
39476       [
39477         "Sudan (‫السودان‬‎)",
39478         "sd",
39479         "249"
39480       ],
39481       [
39482         "Suriname",
39483         "sr",
39484         "597"
39485       ],
39486       [
39487         "Svalbard and Jan Mayen",
39488         "sj",
39489         "47",
39490         1
39491       ],
39492       [
39493         "Swaziland",
39494         "sz",
39495         "268"
39496       ],
39497       [
39498         "Sweden (Sverige)",
39499         "se",
39500         "46"
39501       ],
39502       [
39503         "Switzerland (Schweiz)",
39504         "ch",
39505         "41"
39506       ],
39507       [
39508         "Syria (‫سوريا‬‎)",
39509         "sy",
39510         "963"
39511       ],
39512       [
39513         "Taiwan (台灣)",
39514         "tw",
39515         "886"
39516       ],
39517       [
39518         "Tajikistan",
39519         "tj",
39520         "992"
39521       ],
39522       [
39523         "Tanzania",
39524         "tz",
39525         "255"
39526       ],
39527       [
39528         "Thailand (ไทย)",
39529         "th",
39530         "66"
39531       ],
39532       [
39533         "Timor-Leste",
39534         "tl",
39535         "670"
39536       ],
39537       [
39538         "Togo",
39539         "tg",
39540         "228"
39541       ],
39542       [
39543         "Tokelau",
39544         "tk",
39545         "690"
39546       ],
39547       [
39548         "Tonga",
39549         "to",
39550         "676"
39551       ],
39552       [
39553         "Trinidad and Tobago",
39554         "tt",
39555         "1868"
39556       ],
39557       [
39558         "Tunisia (‫تونس‬‎)",
39559         "tn",
39560         "216"
39561       ],
39562       [
39563         "Turkey (Türkiye)",
39564         "tr",
39565         "90"
39566       ],
39567       [
39568         "Turkmenistan",
39569         "tm",
39570         "993"
39571       ],
39572       [
39573         "Turks and Caicos Islands",
39574         "tc",
39575         "1649"
39576       ],
39577       [
39578         "Tuvalu",
39579         "tv",
39580         "688"
39581       ],
39582       [
39583         "U.S. Virgin Islands",
39584         "vi",
39585         "1340"
39586       ],
39587       [
39588         "Uganda",
39589         "ug",
39590         "256"
39591       ],
39592       [
39593         "Ukraine (Україна)",
39594         "ua",
39595         "380"
39596       ],
39597       [
39598         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39599         "ae",
39600         "971"
39601       ],
39602       [
39603         "United Kingdom",
39604         "gb",
39605         "44",
39606         0
39607       ],
39608       [
39609         "United States",
39610         "us",
39611         "1",
39612         0
39613       ],
39614       [
39615         "Uruguay",
39616         "uy",
39617         "598"
39618       ],
39619       [
39620         "Uzbekistan (Oʻzbekiston)",
39621         "uz",
39622         "998"
39623       ],
39624       [
39625         "Vanuatu",
39626         "vu",
39627         "678"
39628       ],
39629       [
39630         "Vatican City (Città del Vaticano)",
39631         "va",
39632         "39",
39633         1
39634       ],
39635       [
39636         "Venezuela",
39637         "ve",
39638         "58"
39639       ],
39640       [
39641         "Vietnam (Việt Nam)",
39642         "vn",
39643         "84"
39644       ],
39645       [
39646         "Wallis and Futuna (Wallis-et-Futuna)",
39647         "wf",
39648         "681"
39649       ],
39650       [
39651         "Western Sahara (‫الصحراء الغربية‬‎)",
39652         "eh",
39653         "212",
39654         1
39655       ],
39656       [
39657         "Yemen (‫اليمن‬‎)",
39658         "ye",
39659         "967"
39660       ],
39661       [
39662         "Zambia",
39663         "zm",
39664         "260"
39665       ],
39666       [
39667         "Zimbabwe",
39668         "zw",
39669         "263"
39670       ],
39671       [
39672         "Åland Islands",
39673         "ax",
39674         "358",
39675         1
39676       ]
39677   ];
39678   
39679   return d;
39680 }/**
39681 *    This script refer to:
39682 *    Title: International Telephone Input
39683 *    Author: Jack O'Connor
39684 *    Code version:  v12.1.12
39685 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39686 **/
39687
39688 /**
39689  * @class Roo.bootstrap.PhoneInput
39690  * @extends Roo.bootstrap.TriggerField
39691  * An input with International dial-code selection
39692  
39693  * @cfg {String} defaultDialCode default '+852'
39694  * @cfg {Array} preferedCountries default []
39695   
39696  * @constructor
39697  * Create a new PhoneInput.
39698  * @param {Object} config Configuration options
39699  */
39700
39701 Roo.bootstrap.PhoneInput = function(config) {
39702     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39703 };
39704
39705 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39706         
39707         listWidth: undefined,
39708         
39709         selectedClass: 'active',
39710         
39711         invalidClass : "has-warning",
39712         
39713         validClass: 'has-success',
39714         
39715         allowed: '0123456789',
39716         
39717         /**
39718          * @cfg {String} defaultDialCode The default dial code when initializing the input
39719          */
39720         defaultDialCode: '+852',
39721         
39722         /**
39723          * @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
39724          */
39725         preferedCountries: false,
39726         
39727         getAutoCreate : function()
39728         {
39729             var data = Roo.bootstrap.PhoneInputData();
39730             var align = this.labelAlign || this.parentLabelAlign();
39731             var id = Roo.id();
39732             
39733             this.allCountries = [];
39734             this.dialCodeMapping = [];
39735             
39736             for (var i = 0; i < data.length; i++) {
39737               var c = data[i];
39738               this.allCountries[i] = {
39739                 name: c[0],
39740                 iso2: c[1],
39741                 dialCode: c[2],
39742                 priority: c[3] || 0,
39743                 areaCodes: c[4] || null
39744               };
39745               this.dialCodeMapping[c[2]] = {
39746                   name: c[0],
39747                   iso2: c[1],
39748                   priority: c[3] || 0,
39749                   areaCodes: c[4] || null
39750               };
39751             }
39752             
39753             var cfg = {
39754                 cls: 'form-group',
39755                 cn: []
39756             };
39757             
39758             var input =  {
39759                 tag: 'input',
39760                 id : id,
39761                 cls : 'form-control tel-input',
39762                 autocomplete: 'new-password'
39763             };
39764             
39765             var hiddenInput = {
39766                 tag: 'input',
39767                 type: 'hidden',
39768                 cls: 'hidden-tel-input'
39769             };
39770             
39771             if (this.name) {
39772                 hiddenInput.name = this.name;
39773             }
39774             
39775             if (this.disabled) {
39776                 input.disabled = true;
39777             }
39778             
39779             var flag_container = {
39780                 tag: 'div',
39781                 cls: 'flag-box',
39782                 cn: [
39783                     {
39784                         tag: 'div',
39785                         cls: 'flag'
39786                     },
39787                     {
39788                         tag: 'div',
39789                         cls: 'caret'
39790                     }
39791                 ]
39792             };
39793             
39794             var box = {
39795                 tag: 'div',
39796                 cls: this.hasFeedback ? 'has-feedback' : '',
39797                 cn: [
39798                     hiddenInput,
39799                     input,
39800                     {
39801                         tag: 'input',
39802                         cls: 'dial-code-holder',
39803                         disabled: true
39804                     }
39805                 ]
39806             };
39807             
39808             var container = {
39809                 cls: 'roo-select2-container input-group',
39810                 cn: [
39811                     flag_container,
39812                     box
39813                 ]
39814             };
39815             
39816             if (this.fieldLabel.length) {
39817                 var indicator = {
39818                     tag: 'i',
39819                     tooltip: 'This field is required'
39820                 };
39821                 
39822                 var label = {
39823                     tag: 'label',
39824                     'for':  id,
39825                     cls: 'control-label',
39826                     cn: []
39827                 };
39828                 
39829                 var label_text = {
39830                     tag: 'span',
39831                     html: this.fieldLabel
39832                 };
39833                 
39834                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39835                 label.cn = [
39836                     indicator,
39837                     label_text
39838                 ];
39839                 
39840                 if(this.indicatorpos == 'right') {
39841                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39842                     label.cn = [
39843                         label_text,
39844                         indicator
39845                     ];
39846                 }
39847                 
39848                 if(align == 'left') {
39849                     container = {
39850                         tag: 'div',
39851                         cn: [
39852                             container
39853                         ]
39854                     };
39855                     
39856                     if(this.labelWidth > 12){
39857                         label.style = "width: " + this.labelWidth + 'px';
39858                     }
39859                     if(this.labelWidth < 13 && this.labelmd == 0){
39860                         this.labelmd = this.labelWidth;
39861                     }
39862                     if(this.labellg > 0){
39863                         label.cls += ' col-lg-' + this.labellg;
39864                         input.cls += ' col-lg-' + (12 - this.labellg);
39865                     }
39866                     if(this.labelmd > 0){
39867                         label.cls += ' col-md-' + this.labelmd;
39868                         container.cls += ' col-md-' + (12 - this.labelmd);
39869                     }
39870                     if(this.labelsm > 0){
39871                         label.cls += ' col-sm-' + this.labelsm;
39872                         container.cls += ' col-sm-' + (12 - this.labelsm);
39873                     }
39874                     if(this.labelxs > 0){
39875                         label.cls += ' col-xs-' + this.labelxs;
39876                         container.cls += ' col-xs-' + (12 - this.labelxs);
39877                     }
39878                 }
39879             }
39880             
39881             cfg.cn = [
39882                 label,
39883                 container
39884             ];
39885             
39886             var settings = this;
39887             
39888             ['xs','sm','md','lg'].map(function(size){
39889                 if (settings[size]) {
39890                     cfg.cls += ' col-' + size + '-' + settings[size];
39891                 }
39892             });
39893             
39894             this.store = new Roo.data.Store({
39895                 proxy : new Roo.data.MemoryProxy({}),
39896                 reader : new Roo.data.JsonReader({
39897                     fields : [
39898                         {
39899                             'name' : 'name',
39900                             'type' : 'string'
39901                         },
39902                         {
39903                             'name' : 'iso2',
39904                             'type' : 'string'
39905                         },
39906                         {
39907                             'name' : 'dialCode',
39908                             'type' : 'string'
39909                         },
39910                         {
39911                             'name' : 'priority',
39912                             'type' : 'string'
39913                         },
39914                         {
39915                             'name' : 'areaCodes',
39916                             'type' : 'string'
39917                         }
39918                     ]
39919                 })
39920             });
39921             
39922             if(!this.preferedCountries) {
39923                 this.preferedCountries = [
39924                     'hk',
39925                     'gb',
39926                     'us'
39927                 ];
39928             }
39929             
39930             var p = this.preferedCountries.reverse();
39931             
39932             if(p) {
39933                 for (var i = 0; i < p.length; i++) {
39934                     for (var j = 0; j < this.allCountries.length; j++) {
39935                         if(this.allCountries[j].iso2 == p[i]) {
39936                             var t = this.allCountries[j];
39937                             this.allCountries.splice(j,1);
39938                             this.allCountries.unshift(t);
39939                         }
39940                     } 
39941                 }
39942             }
39943             
39944             this.store.proxy.data = {
39945                 success: true,
39946                 data: this.allCountries
39947             };
39948             
39949             return cfg;
39950         },
39951         
39952         initEvents : function()
39953         {
39954             this.createList();
39955             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39956             
39957             this.indicator = this.indicatorEl();
39958             this.flag = this.flagEl();
39959             this.dialCodeHolder = this.dialCodeHolderEl();
39960             
39961             this.trigger = this.el.select('div.flag-box',true).first();
39962             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39963             
39964             var _this = this;
39965             
39966             (function(){
39967                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39968                 _this.list.setWidth(lw);
39969             }).defer(100);
39970             
39971             this.list.on('mouseover', this.onViewOver, this);
39972             this.list.on('mousemove', this.onViewMove, this);
39973             this.inputEl().on("keyup", this.onKeyUp, this);
39974             
39975             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39976
39977             this.view = new Roo.View(this.list, this.tpl, {
39978                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39979             });
39980             
39981             this.view.on('click', this.onViewClick, this);
39982             this.setValue(this.defaultDialCode);
39983         },
39984         
39985         onTriggerClick : function(e)
39986         {
39987             Roo.log('trigger click');
39988             if(this.disabled){
39989                 return;
39990             }
39991             
39992             if(this.isExpanded()){
39993                 this.collapse();
39994                 this.hasFocus = false;
39995             }else {
39996                 this.store.load({});
39997                 this.hasFocus = true;
39998                 this.expand();
39999             }
40000         },
40001         
40002         isExpanded : function()
40003         {
40004             return this.list.isVisible();
40005         },
40006         
40007         collapse : function()
40008         {
40009             if(!this.isExpanded()){
40010                 return;
40011             }
40012             this.list.hide();
40013             Roo.get(document).un('mousedown', this.collapseIf, this);
40014             Roo.get(document).un('mousewheel', this.collapseIf, this);
40015             this.fireEvent('collapse', this);
40016             this.validate();
40017         },
40018         
40019         expand : function()
40020         {
40021             Roo.log('expand');
40022
40023             if(this.isExpanded() || !this.hasFocus){
40024                 return;
40025             }
40026             
40027             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40028             this.list.setWidth(lw);
40029             
40030             this.list.show();
40031             this.restrictHeight();
40032             
40033             Roo.get(document).on('mousedown', this.collapseIf, this);
40034             Roo.get(document).on('mousewheel', this.collapseIf, this);
40035             
40036             this.fireEvent('expand', this);
40037         },
40038         
40039         restrictHeight : function()
40040         {
40041             this.list.alignTo(this.inputEl(), this.listAlign);
40042             this.list.alignTo(this.inputEl(), this.listAlign);
40043         },
40044         
40045         onViewOver : function(e, t)
40046         {
40047             if(this.inKeyMode){
40048                 return;
40049             }
40050             var item = this.view.findItemFromChild(t);
40051             
40052             if(item){
40053                 var index = this.view.indexOf(item);
40054                 this.select(index, false);
40055             }
40056         },
40057
40058         // private
40059         onViewClick : function(view, doFocus, el, e)
40060         {
40061             var index = this.view.getSelectedIndexes()[0];
40062             
40063             var r = this.store.getAt(index);
40064             
40065             if(r){
40066                 this.onSelect(r, index);
40067             }
40068             if(doFocus !== false && !this.blockFocus){
40069                 this.inputEl().focus();
40070             }
40071         },
40072         
40073         onViewMove : function(e, t)
40074         {
40075             this.inKeyMode = false;
40076         },
40077         
40078         select : function(index, scrollIntoView)
40079         {
40080             this.selectedIndex = index;
40081             this.view.select(index);
40082             if(scrollIntoView !== false){
40083                 var el = this.view.getNode(index);
40084                 if(el){
40085                     this.list.scrollChildIntoView(el, false);
40086                 }
40087             }
40088         },
40089         
40090         createList : function()
40091         {
40092             this.list = Roo.get(document.body).createChild({
40093                 tag: 'ul',
40094                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40095                 style: 'display:none'
40096             });
40097             
40098             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40099         },
40100         
40101         collapseIf : function(e)
40102         {
40103             var in_combo  = e.within(this.el);
40104             var in_list =  e.within(this.list);
40105             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40106             
40107             if (in_combo || in_list || is_list) {
40108                 return;
40109             }
40110             this.collapse();
40111         },
40112         
40113         onSelect : function(record, index)
40114         {
40115             if(this.fireEvent('beforeselect', this, record, index) !== false){
40116                 
40117                 this.setFlagClass(record.data.iso2);
40118                 this.setDialCode(record.data.dialCode);
40119                 this.hasFocus = false;
40120                 this.collapse();
40121                 this.fireEvent('select', this, record, index);
40122             }
40123         },
40124         
40125         flagEl : function()
40126         {
40127             var flag = this.el.select('div.flag',true).first();
40128             if(!flag){
40129                 return false;
40130             }
40131             return flag;
40132         },
40133         
40134         dialCodeHolderEl : function()
40135         {
40136             var d = this.el.select('input.dial-code-holder',true).first();
40137             if(!d){
40138                 return false;
40139             }
40140             return d;
40141         },
40142         
40143         setDialCode : function(v)
40144         {
40145             this.dialCodeHolder.dom.value = '+'+v;
40146         },
40147         
40148         setFlagClass : function(n)
40149         {
40150             this.flag.dom.className = 'flag '+n;
40151         },
40152         
40153         getValue : function()
40154         {
40155             var v = this.inputEl().getValue();
40156             if(this.dialCodeHolder) {
40157                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40158             }
40159             return v;
40160         },
40161         
40162         setValue : function(v)
40163         {
40164             var d = this.getDialCode(v);
40165             
40166             //invalid dial code
40167             if(v.length == 0 || !d || d.length == 0) {
40168                 if(this.rendered){
40169                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40170                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40171                 }
40172                 return;
40173             }
40174             
40175             //valid dial code
40176             this.setFlagClass(this.dialCodeMapping[d].iso2);
40177             this.setDialCode(d);
40178             this.inputEl().dom.value = v.replace('+'+d,'');
40179             this.hiddenEl().dom.value = this.getValue();
40180             
40181             this.validate();
40182         },
40183         
40184         getDialCode : function(v)
40185         {
40186             v = v ||  '';
40187             
40188             if (v.length == 0) {
40189                 return this.dialCodeHolder.dom.value;
40190             }
40191             
40192             var dialCode = "";
40193             if (v.charAt(0) != "+") {
40194                 return false;
40195             }
40196             var numericChars = "";
40197             for (var i = 1; i < v.length; i++) {
40198               var c = v.charAt(i);
40199               if (!isNaN(c)) {
40200                 numericChars += c;
40201                 if (this.dialCodeMapping[numericChars]) {
40202                   dialCode = v.substr(1, i);
40203                 }
40204                 if (numericChars.length == 4) {
40205                   break;
40206                 }
40207               }
40208             }
40209             return dialCode;
40210         },
40211         
40212         reset : function()
40213         {
40214             this.setValue(this.defaultDialCode);
40215             this.validate();
40216         },
40217         
40218         hiddenEl : function()
40219         {
40220             return this.el.select('input.hidden-tel-input',true).first();
40221         },
40222         
40223         onKeyUp : function(e){
40224             
40225             var k = e.getKey();
40226             var c = e.getCharCode();
40227             
40228             if(
40229                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40230                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40231             ){
40232                 e.stopEvent();
40233             }
40234             
40235             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40236             //     return;
40237             // }
40238             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40239                 e.stopEvent();
40240             }
40241             
40242             this.setValue(this.getValue());
40243         }
40244         
40245 });
40246 /**
40247  * @class Roo.bootstrap.MoneyField
40248  * @extends Roo.bootstrap.ComboBox
40249  * Bootstrap MoneyField class
40250  * 
40251  * @constructor
40252  * Create a new MoneyField.
40253  * @param {Object} config Configuration options
40254  */
40255
40256 Roo.bootstrap.MoneyField = function(config) {
40257     
40258     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40259     
40260 };
40261
40262 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40263     
40264     /**
40265      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40266      */
40267     allowDecimals : true,
40268     /**
40269      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40270      */
40271     decimalSeparator : ".",
40272     /**
40273      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40274      */
40275     decimalPrecision : 0,
40276     /**
40277      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40278      */
40279     allowNegative : true,
40280     /**
40281      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40282      */
40283     allowZero: true,
40284     /**
40285      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40286      */
40287     minValue : Number.NEGATIVE_INFINITY,
40288     /**
40289      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40290      */
40291     maxValue : Number.MAX_VALUE,
40292     /**
40293      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40294      */
40295     minText : "The minimum value for this field is {0}",
40296     /**
40297      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40298      */
40299     maxText : "The maximum value for this field is {0}",
40300     /**
40301      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40302      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40303      */
40304     nanText : "{0} is not a valid number",
40305     /**
40306      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40307      */
40308     castInt : true,
40309     /**
40310      * @cfg {String} defaults currency of the MoneyField
40311      * value should be in lkey
40312      */
40313     defaultCurrency : false,
40314     /**
40315      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40316      */
40317     thousandsDelimiter : false,
40318     
40319     
40320     inputlg : 9,
40321     inputmd : 9,
40322     inputsm : 9,
40323     inputxs : 6,
40324     
40325     store : false,
40326     
40327     getAutoCreate : function()
40328     {
40329         var align = this.labelAlign || this.parentLabelAlign();
40330         
40331         var id = Roo.id();
40332
40333         var cfg = {
40334             cls: 'form-group',
40335             cn: []
40336         };
40337
40338         var input =  {
40339             tag: 'input',
40340             id : id,
40341             cls : 'form-control roo-money-amount-input',
40342             autocomplete: 'new-password'
40343         };
40344         
40345         var hiddenInput = {
40346             tag: 'input',
40347             type: 'hidden',
40348             id: Roo.id(),
40349             cls: 'hidden-number-input'
40350         };
40351         
40352         if (this.name) {
40353             hiddenInput.name = this.name;
40354         }
40355
40356         if (this.disabled) {
40357             input.disabled = true;
40358         }
40359
40360         var clg = 12 - this.inputlg;
40361         var cmd = 12 - this.inputmd;
40362         var csm = 12 - this.inputsm;
40363         var cxs = 12 - this.inputxs;
40364         
40365         var container = {
40366             tag : 'div',
40367             cls : 'row roo-money-field',
40368             cn : [
40369                 {
40370                     tag : 'div',
40371                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40372                     cn : [
40373                         {
40374                             tag : 'div',
40375                             cls: 'roo-select2-container input-group',
40376                             cn: [
40377                                 {
40378                                     tag : 'input',
40379                                     cls : 'form-control roo-money-currency-input',
40380                                     autocomplete: 'new-password',
40381                                     readOnly : 1,
40382                                     name : this.currencyName
40383                                 },
40384                                 {
40385                                     tag :'span',
40386                                     cls : 'input-group-addon',
40387                                     cn : [
40388                                         {
40389                                             tag: 'span',
40390                                             cls: 'caret'
40391                                         }
40392                                     ]
40393                                 }
40394                             ]
40395                         }
40396                     ]
40397                 },
40398                 {
40399                     tag : 'div',
40400                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40401                     cn : [
40402                         {
40403                             tag: 'div',
40404                             cls: this.hasFeedback ? 'has-feedback' : '',
40405                             cn: [
40406                                 input
40407                             ]
40408                         }
40409                     ]
40410                 }
40411             ]
40412             
40413         };
40414         
40415         if (this.fieldLabel.length) {
40416             var indicator = {
40417                 tag: 'i',
40418                 tooltip: 'This field is required'
40419             };
40420
40421             var label = {
40422                 tag: 'label',
40423                 'for':  id,
40424                 cls: 'control-label',
40425                 cn: []
40426             };
40427
40428             var label_text = {
40429                 tag: 'span',
40430                 html: this.fieldLabel
40431             };
40432
40433             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40434             label.cn = [
40435                 indicator,
40436                 label_text
40437             ];
40438
40439             if(this.indicatorpos == 'right') {
40440                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40441                 label.cn = [
40442                     label_text,
40443                     indicator
40444                 ];
40445             }
40446
40447             if(align == 'left') {
40448                 container = {
40449                     tag: 'div',
40450                     cn: [
40451                         container
40452                     ]
40453                 };
40454
40455                 if(this.labelWidth > 12){
40456                     label.style = "width: " + this.labelWidth + 'px';
40457                 }
40458                 if(this.labelWidth < 13 && this.labelmd == 0){
40459                     this.labelmd = this.labelWidth;
40460                 }
40461                 if(this.labellg > 0){
40462                     label.cls += ' col-lg-' + this.labellg;
40463                     input.cls += ' col-lg-' + (12 - this.labellg);
40464                 }
40465                 if(this.labelmd > 0){
40466                     label.cls += ' col-md-' + this.labelmd;
40467                     container.cls += ' col-md-' + (12 - this.labelmd);
40468                 }
40469                 if(this.labelsm > 0){
40470                     label.cls += ' col-sm-' + this.labelsm;
40471                     container.cls += ' col-sm-' + (12 - this.labelsm);
40472                 }
40473                 if(this.labelxs > 0){
40474                     label.cls += ' col-xs-' + this.labelxs;
40475                     container.cls += ' col-xs-' + (12 - this.labelxs);
40476                 }
40477             }
40478         }
40479
40480         cfg.cn = [
40481             label,
40482             container,
40483             hiddenInput
40484         ];
40485         
40486         var settings = this;
40487
40488         ['xs','sm','md','lg'].map(function(size){
40489             if (settings[size]) {
40490                 cfg.cls += ' col-' + size + '-' + settings[size];
40491             }
40492         });
40493         
40494         return cfg;
40495     },
40496     
40497     initEvents : function()
40498     {
40499         this.indicator = this.indicatorEl();
40500         
40501         this.initCurrencyEvent();
40502         
40503         this.initNumberEvent();
40504     },
40505     
40506     initCurrencyEvent : function()
40507     {
40508         if (!this.store) {
40509             throw "can not find store for combo";
40510         }
40511         
40512         this.store = Roo.factory(this.store, Roo.data);
40513         this.store.parent = this;
40514         
40515         this.createList();
40516         
40517         this.triggerEl = this.el.select('.input-group-addon', true).first();
40518         
40519         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40520         
40521         var _this = this;
40522         
40523         (function(){
40524             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40525             _this.list.setWidth(lw);
40526         }).defer(100);
40527         
40528         this.list.on('mouseover', this.onViewOver, this);
40529         this.list.on('mousemove', this.onViewMove, this);
40530         this.list.on('scroll', this.onViewScroll, this);
40531         
40532         if(!this.tpl){
40533             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40534         }
40535         
40536         this.view = new Roo.View(this.list, this.tpl, {
40537             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40538         });
40539         
40540         this.view.on('click', this.onViewClick, this);
40541         
40542         this.store.on('beforeload', this.onBeforeLoad, this);
40543         this.store.on('load', this.onLoad, this);
40544         this.store.on('loadexception', this.onLoadException, this);
40545         
40546         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40547             "up" : function(e){
40548                 this.inKeyMode = true;
40549                 this.selectPrev();
40550             },
40551
40552             "down" : function(e){
40553                 if(!this.isExpanded()){
40554                     this.onTriggerClick();
40555                 }else{
40556                     this.inKeyMode = true;
40557                     this.selectNext();
40558                 }
40559             },
40560
40561             "enter" : function(e){
40562                 this.collapse();
40563                 
40564                 if(this.fireEvent("specialkey", this, e)){
40565                     this.onViewClick(false);
40566                 }
40567                 
40568                 return true;
40569             },
40570
40571             "esc" : function(e){
40572                 this.collapse();
40573             },
40574
40575             "tab" : function(e){
40576                 this.collapse();
40577                 
40578                 if(this.fireEvent("specialkey", this, e)){
40579                     this.onViewClick(false);
40580                 }
40581                 
40582                 return true;
40583             },
40584
40585             scope : this,
40586
40587             doRelay : function(foo, bar, hname){
40588                 if(hname == 'down' || this.scope.isExpanded()){
40589                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40590                 }
40591                 return true;
40592             },
40593
40594             forceKeyDown: true
40595         });
40596         
40597         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40598         
40599     },
40600     
40601     initNumberEvent : function(e)
40602     {
40603         this.inputEl().on("keydown" , this.fireKey,  this);
40604         this.inputEl().on("focus", this.onFocus,  this);
40605         this.inputEl().on("blur", this.onBlur,  this);
40606         
40607         this.inputEl().relayEvent('keyup', this);
40608         
40609         if(this.indicator){
40610             this.indicator.addClass('invisible');
40611         }
40612  
40613         this.originalValue = this.getValue();
40614         
40615         if(this.validationEvent == 'keyup'){
40616             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40617             this.inputEl().on('keyup', this.filterValidation, this);
40618         }
40619         else if(this.validationEvent !== false){
40620             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40621         }
40622         
40623         if(this.selectOnFocus){
40624             this.on("focus", this.preFocus, this);
40625             
40626         }
40627         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40628             this.inputEl().on("keypress", this.filterKeys, this);
40629         } else {
40630             this.inputEl().relayEvent('keypress', this);
40631         }
40632         
40633         var allowed = "0123456789";
40634         
40635         if(this.allowDecimals){
40636             allowed += this.decimalSeparator;
40637         }
40638         
40639         if(this.allowNegative){
40640             allowed += "-";
40641         }
40642         
40643         if(this.thousandsDelimiter) {
40644             allowed += ",";
40645         }
40646         
40647         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40648         
40649         var keyPress = function(e){
40650             
40651             var k = e.getKey();
40652             
40653             var c = e.getCharCode();
40654             
40655             if(
40656                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40657                     allowed.indexOf(String.fromCharCode(c)) === -1
40658             ){
40659                 e.stopEvent();
40660                 return;
40661             }
40662             
40663             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40664                 return;
40665             }
40666             
40667             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40668                 e.stopEvent();
40669             }
40670         };
40671         
40672         this.inputEl().on("keypress", keyPress, this);
40673         
40674     },
40675     
40676     onTriggerClick : function(e)
40677     {   
40678         if(this.disabled){
40679             return;
40680         }
40681         
40682         this.page = 0;
40683         this.loadNext = false;
40684         
40685         if(this.isExpanded()){
40686             this.collapse();
40687             return;
40688         }
40689         
40690         this.hasFocus = true;
40691         
40692         if(this.triggerAction == 'all') {
40693             this.doQuery(this.allQuery, true);
40694             return;
40695         }
40696         
40697         this.doQuery(this.getRawValue());
40698     },
40699     
40700     getCurrency : function()
40701     {   
40702         var v = this.currencyEl().getValue();
40703         
40704         return v;
40705     },
40706     
40707     restrictHeight : function()
40708     {
40709         this.list.alignTo(this.currencyEl(), this.listAlign);
40710         this.list.alignTo(this.currencyEl(), this.listAlign);
40711     },
40712     
40713     onViewClick : function(view, doFocus, el, e)
40714     {
40715         var index = this.view.getSelectedIndexes()[0];
40716         
40717         var r = this.store.getAt(index);
40718         
40719         if(r){
40720             this.onSelect(r, index);
40721         }
40722     },
40723     
40724     onSelect : function(record, index){
40725         
40726         if(this.fireEvent('beforeselect', this, record, index) !== false){
40727         
40728             this.setFromCurrencyData(index > -1 ? record.data : false);
40729             
40730             this.collapse();
40731             
40732             this.fireEvent('select', this, record, index);
40733         }
40734     },
40735     
40736     setFromCurrencyData : function(o)
40737     {
40738         var currency = '';
40739         
40740         this.lastCurrency = o;
40741         
40742         if (this.currencyField) {
40743             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40744         } else {
40745             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40746         }
40747         
40748         this.lastSelectionText = currency;
40749         
40750         //setting default currency
40751         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40752             this.setCurrency(this.defaultCurrency);
40753             return;
40754         }
40755         
40756         this.setCurrency(currency);
40757     },
40758     
40759     setFromData : function(o)
40760     {
40761         var c = {};
40762         
40763         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40764         
40765         this.setFromCurrencyData(c);
40766         
40767         var value = '';
40768         
40769         if (this.name) {
40770             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40771         } else {
40772             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40773         }
40774         
40775         this.setValue(value);
40776         
40777     },
40778     
40779     setCurrency : function(v)
40780     {   
40781         this.currencyValue = v;
40782         
40783         if(this.rendered){
40784             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40785             this.validate();
40786         }
40787     },
40788     
40789     setValue : function(v)
40790     {
40791         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40792         
40793         this.value = v;
40794         
40795         if(this.rendered){
40796             
40797             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40798             
40799             this.inputEl().dom.value = (v == '') ? '' :
40800                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40801             
40802             if(!this.allowZero && v === '0') {
40803                 this.hiddenEl().dom.value = '';
40804                 this.inputEl().dom.value = '';
40805             }
40806             
40807             this.validate();
40808         }
40809     },
40810     
40811     getRawValue : function()
40812     {
40813         var v = this.inputEl().getValue();
40814         
40815         return v;
40816     },
40817     
40818     getValue : function()
40819     {
40820         return this.fixPrecision(this.parseValue(this.getRawValue()));
40821     },
40822     
40823     parseValue : function(value)
40824     {
40825         if(this.thousandsDelimiter) {
40826             value += "";
40827             r = new RegExp(",", "g");
40828             value = value.replace(r, "");
40829         }
40830         
40831         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40832         return isNaN(value) ? '' : value;
40833         
40834     },
40835     
40836     fixPrecision : function(value)
40837     {
40838         if(this.thousandsDelimiter) {
40839             value += "";
40840             r = new RegExp(",", "g");
40841             value = value.replace(r, "");
40842         }
40843         
40844         var nan = isNaN(value);
40845         
40846         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40847             return nan ? '' : value;
40848         }
40849         return parseFloat(value).toFixed(this.decimalPrecision);
40850     },
40851     
40852     decimalPrecisionFcn : function(v)
40853     {
40854         return Math.floor(v);
40855     },
40856     
40857     validateValue : function(value)
40858     {
40859         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40860             return false;
40861         }
40862         
40863         var num = this.parseValue(value);
40864         
40865         if(isNaN(num)){
40866             this.markInvalid(String.format(this.nanText, value));
40867             return false;
40868         }
40869         
40870         if(num < this.minValue){
40871             this.markInvalid(String.format(this.minText, this.minValue));
40872             return false;
40873         }
40874         
40875         if(num > this.maxValue){
40876             this.markInvalid(String.format(this.maxText, this.maxValue));
40877             return false;
40878         }
40879         
40880         return true;
40881     },
40882     
40883     validate : function()
40884     {
40885         if(this.disabled || this.allowBlank){
40886             this.markValid();
40887             return true;
40888         }
40889         
40890         var currency = this.getCurrency();
40891         
40892         if(this.validateValue(this.getRawValue()) && currency.length){
40893             this.markValid();
40894             return true;
40895         }
40896         
40897         this.markInvalid();
40898         return false;
40899     },
40900     
40901     getName: function()
40902     {
40903         return this.name;
40904     },
40905     
40906     beforeBlur : function()
40907     {
40908         if(!this.castInt){
40909             return;
40910         }
40911         
40912         var v = this.parseValue(this.getRawValue());
40913         
40914         if(v || v == 0){
40915             this.setValue(v);
40916         }
40917     },
40918     
40919     onBlur : function()
40920     {
40921         this.beforeBlur();
40922         
40923         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40924             //this.el.removeClass(this.focusClass);
40925         }
40926         
40927         this.hasFocus = false;
40928         
40929         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40930             this.validate();
40931         }
40932         
40933         var v = this.getValue();
40934         
40935         if(String(v) !== String(this.startValue)){
40936             this.fireEvent('change', this, v, this.startValue);
40937         }
40938         
40939         this.fireEvent("blur", this);
40940     },
40941     
40942     inputEl : function()
40943     {
40944         return this.el.select('.roo-money-amount-input', true).first();
40945     },
40946     
40947     currencyEl : function()
40948     {
40949         return this.el.select('.roo-money-currency-input', true).first();
40950     },
40951     
40952     hiddenEl : function()
40953     {
40954         return this.el.select('input.hidden-number-input',true).first();
40955     }
40956     
40957 });