roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     hide: function() {
921        
922      
923         this.el.hide();   
924     },
925     show: function() {
926        
927         this.el.show();   
928     },
929     setWeight : function(str)
930     {
931         this.el.removeClass(this.weightClass);
932         this.el.addClass('btn-' + str);        
933     }
934     
935     
936 });
937
938  /*
939  * - LGPL
940  *
941  * column
942  * 
943  */
944
945 /**
946  * @class Roo.bootstrap.Column
947  * @extends Roo.bootstrap.Component
948  * Bootstrap Column class
949  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
950  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
951  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
952  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
953  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
954  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
955  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
956  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
957  *
958  * 
959  * @cfg {Boolean} hidden (true|false) hide the element
960  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
961  * @cfg {String} fa (ban|check|...) font awesome icon
962  * @cfg {Number} fasize (1|2|....) font awsome size
963
964  * @cfg {String} icon (info-sign|check|...) glyphicon name
965
966  * @cfg {String} html content of column.
967  * 
968  * @constructor
969  * Create a new Column
970  * @param {Object} config The config object
971  */
972
973 Roo.bootstrap.Column = function(config){
974     Roo.bootstrap.Column.superclass.constructor.call(this, config);
975 };
976
977 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
978     
979     xs: false,
980     sm: false,
981     md: false,
982     lg: false,
983     xsoff: false,
984     smoff: false,
985     mdoff: false,
986     lgoff: false,
987     html: '',
988     offset: 0,
989     alert: false,
990     fa: false,
991     icon : false,
992     hidden : false,
993     fasize : 1,
994     
995     getAutoCreate : function(){
996         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
997         
998         cfg = {
999             tag: 'div',
1000             cls: 'column'
1001         };
1002         
1003         var settings=this;
1004         ['xs','sm','md','lg'].map(function(size){
1005             //Roo.log( size + ':' + settings[size]);
1006             
1007             if (settings[size+'off'] !== false) {
1008                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1009             }
1010             
1011             if (settings[size] === false) {
1012                 return;
1013             }
1014             
1015             if (!settings[size]) { // 0 = hidden
1016                 cfg.cls += ' hidden-' + size;
1017                 return;
1018             }
1019             cfg.cls += ' col-' + size + '-' + settings[size];
1020             
1021         });
1022         
1023         if (this.hidden) {
1024             cfg.cls += ' hidden';
1025         }
1026         
1027         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028             cfg.cls +=' alert alert-' + this.alert;
1029         }
1030         
1031         
1032         if (this.html.length) {
1033             cfg.html = this.html;
1034         }
1035         if (this.fa) {
1036             var fasize = '';
1037             if (this.fasize > 1) {
1038                 fasize = ' fa-' + this.fasize + 'x';
1039             }
1040             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1041             
1042             
1043         }
1044         if (this.icon) {
1045             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1046         }
1047         
1048         return cfg;
1049     }
1050    
1051 });
1052
1053  
1054
1055  /*
1056  * - LGPL
1057  *
1058  * page container.
1059  * 
1060  */
1061
1062
1063 /**
1064  * @class Roo.bootstrap.Container
1065  * @extends Roo.bootstrap.Component
1066  * Bootstrap Container class
1067  * @cfg {Boolean} jumbotron is it a jumbotron element
1068  * @cfg {String} html content of element
1069  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1070  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1071  * @cfg {String} header content of header (for panel)
1072  * @cfg {String} footer content of footer (for panel)
1073  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1074  * @cfg {String} tag (header|aside|section) type of HTML tag.
1075  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1076  * @cfg {String} fa font awesome icon
1077  * @cfg {String} icon (info-sign|check|...) glyphicon name
1078  * @cfg {Boolean} hidden (true|false) hide the element
1079  * @cfg {Boolean} expandable (true|false) default false
1080  * @cfg {Boolean} expanded (true|false) default true
1081  * @cfg {String} rheader contet on the right of header
1082  * @cfg {Boolean} clickable (true|false) default false
1083
1084  *     
1085  * @constructor
1086  * Create a new Container
1087  * @param {Object} config The config object
1088  */
1089
1090 Roo.bootstrap.Container = function(config){
1091     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1092     
1093     this.addEvents({
1094         // raw events
1095          /**
1096          * @event expand
1097          * After the panel has been expand
1098          * 
1099          * @param {Roo.bootstrap.Container} this
1100          */
1101         "expand" : true,
1102         /**
1103          * @event collapse
1104          * After the panel has been collapsed
1105          * 
1106          * @param {Roo.bootstrap.Container} this
1107          */
1108         "collapse" : true,
1109         /**
1110          * @event click
1111          * When a element is chick
1112          * @param {Roo.bootstrap.Container} this
1113          * @param {Roo.EventObject} e
1114          */
1115         "click" : true
1116     });
1117 };
1118
1119 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1120     
1121     jumbotron : false,
1122     well: '',
1123     panel : '',
1124     header: '',
1125     footer : '',
1126     sticky: '',
1127     tag : false,
1128     alert : false,
1129     fa: false,
1130     icon : false,
1131     expandable : false,
1132     rheader : '',
1133     expanded : true,
1134     clickable: false,
1135   
1136      
1137     getChildContainer : function() {
1138         
1139         if(!this.el){
1140             return false;
1141         }
1142         
1143         if (this.panel.length) {
1144             return this.el.select('.panel-body',true).first();
1145         }
1146         
1147         return this.el;
1148     },
1149     
1150     
1151     getAutoCreate : function(){
1152         
1153         var cfg = {
1154             tag : this.tag || 'div',
1155             html : '',
1156             cls : ''
1157         };
1158         if (this.jumbotron) {
1159             cfg.cls = 'jumbotron';
1160         }
1161         
1162         
1163         
1164         // - this is applied by the parent..
1165         //if (this.cls) {
1166         //    cfg.cls = this.cls + '';
1167         //}
1168         
1169         if (this.sticky.length) {
1170             
1171             var bd = Roo.get(document.body);
1172             if (!bd.hasClass('bootstrap-sticky')) {
1173                 bd.addClass('bootstrap-sticky');
1174                 Roo.select('html',true).setStyle('height', '100%');
1175             }
1176              
1177             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1178         }
1179         
1180         
1181         if (this.well.length) {
1182             switch (this.well) {
1183                 case 'lg':
1184                 case 'sm':
1185                     cfg.cls +=' well well-' +this.well;
1186                     break;
1187                 default:
1188                     cfg.cls +=' well';
1189                     break;
1190             }
1191         }
1192         
1193         if (this.hidden) {
1194             cfg.cls += ' hidden';
1195         }
1196         
1197         
1198         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1199             cfg.cls +=' alert alert-' + this.alert;
1200         }
1201         
1202         var body = cfg;
1203         
1204         if (this.panel.length) {
1205             cfg.cls += ' panel panel-' + this.panel;
1206             cfg.cn = [];
1207             if (this.header.length) {
1208                 
1209                 var h = [];
1210                 
1211                 if(this.expandable){
1212                     
1213                     cfg.cls = cfg.cls + ' expandable';
1214                     
1215                     h.push({
1216                         tag: 'i',
1217                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1218                     });
1219                     
1220                 }
1221                 
1222                 h.push(
1223                     {
1224                         tag: 'span',
1225                         cls : 'panel-title',
1226                         html : (this.expandable ? '&nbsp;' : '') + this.header
1227                     },
1228                     {
1229                         tag: 'span',
1230                         cls: 'panel-header-right',
1231                         html: this.rheader
1232                     }
1233                 );
1234                 
1235                 cfg.cn.push({
1236                     cls : 'panel-heading',
1237                     style : this.expandable ? 'cursor: pointer' : '',
1238                     cn : h
1239                 });
1240                 
1241             }
1242             
1243             body = false;
1244             cfg.cn.push({
1245                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1246                 html : this.html
1247             });
1248             
1249             
1250             if (this.footer.length) {
1251                 cfg.cn.push({
1252                     cls : 'panel-footer',
1253                     html : this.footer
1254                     
1255                 });
1256             }
1257             
1258         }
1259         
1260         if (body) {
1261             body.html = this.html || cfg.html;
1262             // prefix with the icons..
1263             if (this.fa) {
1264                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1265             }
1266             if (this.icon) {
1267                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1268             }
1269             
1270             
1271         }
1272         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1273             cfg.cls =  'container';
1274         }
1275         
1276         return cfg;
1277     },
1278     
1279     initEvents: function() 
1280     {
1281         if(this.expandable){
1282             var headerEl = this.headerEl();
1283         
1284             if(headerEl){
1285                 headerEl.on('click', this.onToggleClick, this);
1286             }
1287         }
1288         
1289         if(this.clickable){
1290             this.el.on('click', this.onClick, this);
1291         }
1292         
1293     },
1294     
1295     onToggleClick : function()
1296     {
1297         var headerEl = this.headerEl();
1298         
1299         if(!headerEl){
1300             return;
1301         }
1302         
1303         if(this.expanded){
1304             this.collapse();
1305             return;
1306         }
1307         
1308         this.expand();
1309     },
1310     
1311     expand : function()
1312     {
1313         if(this.fireEvent('expand', this)) {
1314             
1315             this.expanded = true;
1316             
1317             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1318             
1319             this.el.select('.panel-body',true).first().removeClass('hide');
1320             
1321             var toggleEl = this.toggleEl();
1322
1323             if(!toggleEl){
1324                 return;
1325             }
1326
1327             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1328         }
1329         
1330     },
1331     
1332     collapse : function()
1333     {
1334         if(this.fireEvent('collapse', this)) {
1335             
1336             this.expanded = false;
1337             
1338             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1339             this.el.select('.panel-body',true).first().addClass('hide');
1340         
1341             var toggleEl = this.toggleEl();
1342
1343             if(!toggleEl){
1344                 return;
1345             }
1346
1347             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1348         }
1349     },
1350     
1351     toggleEl : function()
1352     {
1353         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1354             return;
1355         }
1356         
1357         return this.el.select('.panel-heading .fa',true).first();
1358     },
1359     
1360     headerEl : function()
1361     {
1362         if(!this.el || !this.panel.length || !this.header.length){
1363             return;
1364         }
1365         
1366         return this.el.select('.panel-heading',true).first()
1367     },
1368     
1369     bodyEl : function()
1370     {
1371         if(!this.el || !this.panel.length){
1372             return;
1373         }
1374         
1375         return this.el.select('.panel-body',true).first()
1376     },
1377     
1378     titleEl : function()
1379     {
1380         if(!this.el || !this.panel.length || !this.header.length){
1381             return;
1382         }
1383         
1384         return this.el.select('.panel-title',true).first();
1385     },
1386     
1387     setTitle : function(v)
1388     {
1389         var titleEl = this.titleEl();
1390         
1391         if(!titleEl){
1392             return;
1393         }
1394         
1395         titleEl.dom.innerHTML = v;
1396     },
1397     
1398     getTitle : function()
1399     {
1400         
1401         var titleEl = this.titleEl();
1402         
1403         if(!titleEl){
1404             return '';
1405         }
1406         
1407         return titleEl.dom.innerHTML;
1408     },
1409     
1410     setRightTitle : function(v)
1411     {
1412         var t = this.el.select('.panel-header-right',true).first();
1413         
1414         if(!t){
1415             return;
1416         }
1417         
1418         t.dom.innerHTML = v;
1419     },
1420     
1421     onClick : function(e)
1422     {
1423         e.preventDefault();
1424         
1425         this.fireEvent('click', this, e);
1426     }
1427 });
1428
1429  /*
1430  * - LGPL
1431  *
1432  * image
1433  * 
1434  */
1435
1436
1437 /**
1438  * @class Roo.bootstrap.Img
1439  * @extends Roo.bootstrap.Component
1440  * Bootstrap Img class
1441  * @cfg {Boolean} imgResponsive false | true
1442  * @cfg {String} border rounded | circle | thumbnail
1443  * @cfg {String} src image source
1444  * @cfg {String} alt image alternative text
1445  * @cfg {String} href a tag href
1446  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1447  * @cfg {String} xsUrl xs image source
1448  * @cfg {String} smUrl sm image source
1449  * @cfg {String} mdUrl md image source
1450  * @cfg {String} lgUrl lg image source
1451  * 
1452  * @constructor
1453  * Create a new Input
1454  * @param {Object} config The config object
1455  */
1456
1457 Roo.bootstrap.Img = function(config){
1458     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1459     
1460     this.addEvents({
1461         // img events
1462         /**
1463          * @event click
1464          * The img click event for the img.
1465          * @param {Roo.EventObject} e
1466          */
1467         "click" : true
1468     });
1469 };
1470
1471 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1472     
1473     imgResponsive: true,
1474     border: '',
1475     src: 'about:blank',
1476     href: false,
1477     target: false,
1478     xsUrl: '',
1479     smUrl: '',
1480     mdUrl: '',
1481     lgUrl: '',
1482
1483     getAutoCreate : function()
1484     {   
1485         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1486             return this.createSingleImg();
1487         }
1488         
1489         var cfg = {
1490             tag: 'div',
1491             cls: 'roo-image-responsive-group',
1492             cn: []
1493         };
1494         var _this = this;
1495         
1496         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1497             
1498             if(!_this[size + 'Url']){
1499                 return;
1500             }
1501             
1502             var img = {
1503                 tag: 'img',
1504                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1505                 html: _this.html || cfg.html,
1506                 src: _this[size + 'Url']
1507             };
1508             
1509             img.cls += ' roo-image-responsive-' + size;
1510             
1511             var s = ['xs', 'sm', 'md', 'lg'];
1512             
1513             s.splice(s.indexOf(size), 1);
1514             
1515             Roo.each(s, function(ss){
1516                 img.cls += ' hidden-' + ss;
1517             });
1518             
1519             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1520                 cfg.cls += ' img-' + _this.border;
1521             }
1522             
1523             if(_this.alt){
1524                 cfg.alt = _this.alt;
1525             }
1526             
1527             if(_this.href){
1528                 var a = {
1529                     tag: 'a',
1530                     href: _this.href,
1531                     cn: [
1532                         img
1533                     ]
1534                 };
1535
1536                 if(this.target){
1537                     a.target = _this.target;
1538                 }
1539             }
1540             
1541             cfg.cn.push((_this.href) ? a : img);
1542             
1543         });
1544         
1545         return cfg;
1546     },
1547     
1548     createSingleImg : function()
1549     {
1550         var cfg = {
1551             tag: 'img',
1552             cls: (this.imgResponsive) ? 'img-responsive' : '',
1553             html : null,
1554             src : 'about:blank'  // just incase src get's set to undefined?!?
1555         };
1556         
1557         cfg.html = this.html || cfg.html;
1558         
1559         cfg.src = this.src || cfg.src;
1560         
1561         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1562             cfg.cls += ' img-' + this.border;
1563         }
1564         
1565         if(this.alt){
1566             cfg.alt = this.alt;
1567         }
1568         
1569         if(this.href){
1570             var a = {
1571                 tag: 'a',
1572                 href: this.href,
1573                 cn: [
1574                     cfg
1575                 ]
1576             };
1577             
1578             if(this.target){
1579                 a.target = this.target;
1580             }
1581             
1582         }
1583         
1584         return (this.href) ? a : cfg;
1585     },
1586     
1587     initEvents: function() 
1588     {
1589         if(!this.href){
1590             this.el.on('click', this.onClick, this);
1591         }
1592         
1593     },
1594     
1595     onClick : function(e)
1596     {
1597         Roo.log('img onclick');
1598         this.fireEvent('click', this, e);
1599     },
1600     /**
1601      * Sets the url of the image - used to update it
1602      * @param {String} url the url of the image
1603      */
1604     
1605     setSrc : function(url)
1606     {
1607         this.src =  url;
1608         
1609         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1610             this.el.dom.src =  url;
1611             return;
1612         }
1613         
1614         this.el.select('img', true).first().dom.src =  url;
1615     }
1616     
1617     
1618    
1619 });
1620
1621  /*
1622  * - LGPL
1623  *
1624  * image
1625  * 
1626  */
1627
1628
1629 /**
1630  * @class Roo.bootstrap.Link
1631  * @extends Roo.bootstrap.Component
1632  * Bootstrap Link Class
1633  * @cfg {String} alt image alternative text
1634  * @cfg {String} href a tag href
1635  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1636  * @cfg {String} html the content of the link.
1637  * @cfg {String} anchor name for the anchor link
1638  * @cfg {String} fa - favicon
1639
1640  * @cfg {Boolean} preventDefault (true | false) default false
1641
1642  * 
1643  * @constructor
1644  * Create a new Input
1645  * @param {Object} config The config object
1646  */
1647
1648 Roo.bootstrap.Link = function(config){
1649     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1650     
1651     this.addEvents({
1652         // img events
1653         /**
1654          * @event click
1655          * The img click event for the img.
1656          * @param {Roo.EventObject} e
1657          */
1658         "click" : true
1659     });
1660 };
1661
1662 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1663     
1664     href: false,
1665     target: false,
1666     preventDefault: false,
1667     anchor : false,
1668     alt : false,
1669     fa: false,
1670
1671
1672     getAutoCreate : function()
1673     {
1674         var html = this.html || '';
1675         
1676         if (this.fa !== false) {
1677             html = '<i class="fa fa-' + this.fa + '"></i>';
1678         }
1679         var cfg = {
1680             tag: 'a'
1681         };
1682         // anchor's do not require html/href...
1683         if (this.anchor === false) {
1684             cfg.html = html;
1685             cfg.href = this.href || '#';
1686         } else {
1687             cfg.name = this.anchor;
1688             if (this.html !== false || this.fa !== false) {
1689                 cfg.html = html;
1690             }
1691             if (this.href !== false) {
1692                 cfg.href = this.href;
1693             }
1694         }
1695         
1696         if(this.alt !== false){
1697             cfg.alt = this.alt;
1698         }
1699         
1700         
1701         if(this.target !== false) {
1702             cfg.target = this.target;
1703         }
1704         
1705         return cfg;
1706     },
1707     
1708     initEvents: function() {
1709         
1710         if(!this.href || this.preventDefault){
1711             this.el.on('click', this.onClick, this);
1712         }
1713     },
1714     
1715     onClick : function(e)
1716     {
1717         if(this.preventDefault){
1718             e.preventDefault();
1719         }
1720         //Roo.log('img onclick');
1721         this.fireEvent('click', this, e);
1722     }
1723    
1724 });
1725
1726  /*
1727  * - LGPL
1728  *
1729  * header
1730  * 
1731  */
1732
1733 /**
1734  * @class Roo.bootstrap.Header
1735  * @extends Roo.bootstrap.Component
1736  * Bootstrap Header class
1737  * @cfg {String} html content of header
1738  * @cfg {Number} level (1|2|3|4|5|6) default 1
1739  * 
1740  * @constructor
1741  * Create a new Header
1742  * @param {Object} config The config object
1743  */
1744
1745
1746 Roo.bootstrap.Header  = function(config){
1747     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1748 };
1749
1750 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1751     
1752     //href : false,
1753     html : false,
1754     level : 1,
1755     
1756     
1757     
1758     getAutoCreate : function(){
1759         
1760         
1761         
1762         var cfg = {
1763             tag: 'h' + (1 *this.level),
1764             html: this.html || ''
1765         } ;
1766         
1767         return cfg;
1768     }
1769    
1770 });
1771
1772  
1773
1774  /*
1775  * Based on:
1776  * Ext JS Library 1.1.1
1777  * Copyright(c) 2006-2007, Ext JS, LLC.
1778  *
1779  * Originally Released Under LGPL - original licence link has changed is not relivant.
1780  *
1781  * Fork - LGPL
1782  * <script type="text/javascript">
1783  */
1784  
1785 /**
1786  * @class Roo.bootstrap.MenuMgr
1787  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1788  * @singleton
1789  */
1790 Roo.bootstrap.MenuMgr = function(){
1791    var menus, active, groups = {}, attached = false, lastShow = new Date();
1792
1793    // private - called when first menu is created
1794    function init(){
1795        menus = {};
1796        active = new Roo.util.MixedCollection();
1797        Roo.get(document).addKeyListener(27, function(){
1798            if(active.length > 0){
1799                hideAll();
1800            }
1801        });
1802    }
1803
1804    // private
1805    function hideAll(){
1806        if(active && active.length > 0){
1807            var c = active.clone();
1808            c.each(function(m){
1809                m.hide();
1810            });
1811        }
1812    }
1813
1814    // private
1815    function onHide(m){
1816        active.remove(m);
1817        if(active.length < 1){
1818            Roo.get(document).un("mouseup", onMouseDown);
1819             
1820            attached = false;
1821        }
1822    }
1823
1824    // private
1825    function onShow(m){
1826        var last = active.last();
1827        lastShow = new Date();
1828        active.add(m);
1829        if(!attached){
1830           Roo.get(document).on("mouseup", onMouseDown);
1831            
1832            attached = true;
1833        }
1834        if(m.parentMenu){
1835           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1836           m.parentMenu.activeChild = m;
1837        }else if(last && last.isVisible()){
1838           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1839        }
1840    }
1841
1842    // private
1843    function onBeforeHide(m){
1844        if(m.activeChild){
1845            m.activeChild.hide();
1846        }
1847        if(m.autoHideTimer){
1848            clearTimeout(m.autoHideTimer);
1849            delete m.autoHideTimer;
1850        }
1851    }
1852
1853    // private
1854    function onBeforeShow(m){
1855        var pm = m.parentMenu;
1856        if(!pm && !m.allowOtherMenus){
1857            hideAll();
1858        }else if(pm && pm.activeChild && active != m){
1859            pm.activeChild.hide();
1860        }
1861    }
1862
1863    // private this should really trigger on mouseup..
1864    function onMouseDown(e){
1865         Roo.log("on Mouse Up");
1866         
1867         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1868             Roo.log("MenuManager hideAll");
1869             hideAll();
1870             e.stopEvent();
1871         }
1872         
1873         
1874    }
1875
1876    // private
1877    function onBeforeCheck(mi, state){
1878        if(state){
1879            var g = groups[mi.group];
1880            for(var i = 0, l = g.length; i < l; i++){
1881                if(g[i] != mi){
1882                    g[i].setChecked(false);
1883                }
1884            }
1885        }
1886    }
1887
1888    return {
1889
1890        /**
1891         * Hides all menus that are currently visible
1892         */
1893        hideAll : function(){
1894             hideAll();  
1895        },
1896
1897        // private
1898        register : function(menu){
1899            if(!menus){
1900                init();
1901            }
1902            menus[menu.id] = menu;
1903            menu.on("beforehide", onBeforeHide);
1904            menu.on("hide", onHide);
1905            menu.on("beforeshow", onBeforeShow);
1906            menu.on("show", onShow);
1907            var g = menu.group;
1908            if(g && menu.events["checkchange"]){
1909                if(!groups[g]){
1910                    groups[g] = [];
1911                }
1912                groups[g].push(menu);
1913                menu.on("checkchange", onCheck);
1914            }
1915        },
1916
1917         /**
1918          * Returns a {@link Roo.menu.Menu} object
1919          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1920          * be used to generate and return a new Menu instance.
1921          */
1922        get : function(menu){
1923            if(typeof menu == "string"){ // menu id
1924                return menus[menu];
1925            }else if(menu.events){  // menu instance
1926                return menu;
1927            }
1928            /*else if(typeof menu.length == 'number'){ // array of menu items?
1929                return new Roo.bootstrap.Menu({items:menu});
1930            }else{ // otherwise, must be a config
1931                return new Roo.bootstrap.Menu(menu);
1932            }
1933            */
1934            return false;
1935        },
1936
1937        // private
1938        unregister : function(menu){
1939            delete menus[menu.id];
1940            menu.un("beforehide", onBeforeHide);
1941            menu.un("hide", onHide);
1942            menu.un("beforeshow", onBeforeShow);
1943            menu.un("show", onShow);
1944            var g = menu.group;
1945            if(g && menu.events["checkchange"]){
1946                groups[g].remove(menu);
1947                menu.un("checkchange", onCheck);
1948            }
1949        },
1950
1951        // private
1952        registerCheckable : function(menuItem){
1953            var g = menuItem.group;
1954            if(g){
1955                if(!groups[g]){
1956                    groups[g] = [];
1957                }
1958                groups[g].push(menuItem);
1959                menuItem.on("beforecheckchange", onBeforeCheck);
1960            }
1961        },
1962
1963        // private
1964        unregisterCheckable : function(menuItem){
1965            var g = menuItem.group;
1966            if(g){
1967                groups[g].remove(menuItem);
1968                menuItem.un("beforecheckchange", onBeforeCheck);
1969            }
1970        }
1971    };
1972 }();/*
1973  * - LGPL
1974  *
1975  * menu
1976  * 
1977  */
1978
1979 /**
1980  * @class Roo.bootstrap.Menu
1981  * @extends Roo.bootstrap.Component
1982  * Bootstrap Menu class - container for MenuItems
1983  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1984  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1985  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1986  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1987  * 
1988  * @constructor
1989  * Create a new Menu
1990  * @param {Object} config The config object
1991  */
1992
1993
1994 Roo.bootstrap.Menu = function(config){
1995     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1996     if (this.registerMenu && this.type != 'treeview')  {
1997         Roo.bootstrap.MenuMgr.register(this);
1998     }
1999     this.addEvents({
2000         /**
2001          * @event beforeshow
2002          * Fires before this menu is displayed
2003          * @param {Roo.menu.Menu} this
2004          */
2005         beforeshow : true,
2006         /**
2007          * @event beforehide
2008          * Fires before this menu is hidden
2009          * @param {Roo.menu.Menu} this
2010          */
2011         beforehide : true,
2012         /**
2013          * @event show
2014          * Fires after this menu is displayed
2015          * @param {Roo.menu.Menu} this
2016          */
2017         show : true,
2018         /**
2019          * @event hide
2020          * Fires after this menu is hidden
2021          * @param {Roo.menu.Menu} this
2022          */
2023         hide : true,
2024         /**
2025          * @event click
2026          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2027          * @param {Roo.menu.Menu} this
2028          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2029          * @param {Roo.EventObject} e
2030          */
2031         click : true,
2032         /**
2033          * @event mouseover
2034          * Fires when the mouse is hovering over this menu
2035          * @param {Roo.menu.Menu} this
2036          * @param {Roo.EventObject} e
2037          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038          */
2039         mouseover : true,
2040         /**
2041          * @event mouseout
2042          * Fires when the mouse exits this menu
2043          * @param {Roo.menu.Menu} this
2044          * @param {Roo.EventObject} e
2045          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2046          */
2047         mouseout : true,
2048         /**
2049          * @event itemclick
2050          * Fires when a menu item contained in this menu is clicked
2051          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2052          * @param {Roo.EventObject} e
2053          */
2054         itemclick: true
2055     });
2056     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2057 };
2058
2059 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2060     
2061    /// html : false,
2062     //align : '',
2063     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2064     type: false,
2065     /**
2066      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2067      */
2068     registerMenu : true,
2069     
2070     menuItems :false, // stores the menu items..
2071     
2072     hidden:true,
2073         
2074     parentMenu : false,
2075     
2076     stopEvent : true,
2077     
2078     isLink : false,
2079     
2080     getChildContainer : function() {
2081         return this.el;  
2082     },
2083     
2084     getAutoCreate : function(){
2085          
2086         //if (['right'].indexOf(this.align)!==-1) {
2087         //    cfg.cn[1].cls += ' pull-right'
2088         //}
2089         
2090         
2091         var cfg = {
2092             tag : 'ul',
2093             cls : 'dropdown-menu' ,
2094             style : 'z-index:1000'
2095             
2096         };
2097         
2098         if (this.type === 'submenu') {
2099             cfg.cls = 'submenu active';
2100         }
2101         if (this.type === 'treeview') {
2102             cfg.cls = 'treeview-menu';
2103         }
2104         
2105         return cfg;
2106     },
2107     initEvents : function() {
2108         
2109        // Roo.log("ADD event");
2110        // Roo.log(this.triggerEl.dom);
2111         
2112         this.triggerEl.on('click', this.onTriggerClick, this);
2113         
2114         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2115         
2116         this.triggerEl.addClass('dropdown-toggle');
2117         
2118         if (Roo.isTouch) {
2119             this.el.on('touchstart'  , this.onTouch, this);
2120         }
2121         this.el.on('click' , this.onClick, this);
2122
2123         this.el.on("mouseover", this.onMouseOver, this);
2124         this.el.on("mouseout", this.onMouseOut, this);
2125         
2126     },
2127     
2128     findTargetItem : function(e)
2129     {
2130         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2131         if(!t){
2132             return false;
2133         }
2134         //Roo.log(t);         Roo.log(t.id);
2135         if(t && t.id){
2136             //Roo.log(this.menuitems);
2137             return this.menuitems.get(t.id);
2138             
2139             //return this.items.get(t.menuItemId);
2140         }
2141         
2142         return false;
2143     },
2144     
2145     onTouch : function(e) 
2146     {
2147         Roo.log("menu.onTouch");
2148         //e.stopEvent(); this make the user popdown broken
2149         this.onClick(e);
2150     },
2151     
2152     onClick : function(e)
2153     {
2154         Roo.log("menu.onClick");
2155         
2156         var t = this.findTargetItem(e);
2157         if(!t || t.isContainer){
2158             return;
2159         }
2160         Roo.log(e);
2161         /*
2162         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2163             if(t == this.activeItem && t.shouldDeactivate(e)){
2164                 this.activeItem.deactivate();
2165                 delete this.activeItem;
2166                 return;
2167             }
2168             if(t.canActivate){
2169                 this.setActiveItem(t, true);
2170             }
2171             return;
2172             
2173             
2174         }
2175         */
2176        
2177         Roo.log('pass click event');
2178         
2179         t.onClick(e);
2180         
2181         this.fireEvent("click", this, t, e);
2182         
2183         var _this = this;
2184         
2185         if(!t.href.length || t.href == '#'){
2186             (function() { _this.hide(); }).defer(100);
2187         }
2188         
2189     },
2190     
2191     onMouseOver : function(e){
2192         var t  = this.findTargetItem(e);
2193         //Roo.log(t);
2194         //if(t){
2195         //    if(t.canActivate && !t.disabled){
2196         //        this.setActiveItem(t, true);
2197         //    }
2198         //}
2199         
2200         this.fireEvent("mouseover", this, e, t);
2201     },
2202     isVisible : function(){
2203         return !this.hidden;
2204     },
2205      onMouseOut : function(e){
2206         var t  = this.findTargetItem(e);
2207         
2208         //if(t ){
2209         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2210         //        this.activeItem.deactivate();
2211         //        delete this.activeItem;
2212         //    }
2213         //}
2214         this.fireEvent("mouseout", this, e, t);
2215     },
2216     
2217     
2218     /**
2219      * Displays this menu relative to another element
2220      * @param {String/HTMLElement/Roo.Element} element The element to align to
2221      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2222      * the element (defaults to this.defaultAlign)
2223      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2224      */
2225     show : function(el, pos, parentMenu){
2226         this.parentMenu = parentMenu;
2227         if(!this.el){
2228             this.render();
2229         }
2230         this.fireEvent("beforeshow", this);
2231         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2232     },
2233      /**
2234      * Displays this menu at a specific xy position
2235      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2236      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2237      */
2238     showAt : function(xy, parentMenu, /* private: */_e){
2239         this.parentMenu = parentMenu;
2240         if(!this.el){
2241             this.render();
2242         }
2243         if(_e !== false){
2244             this.fireEvent("beforeshow", this);
2245             //xy = this.el.adjustForConstraints(xy);
2246         }
2247         
2248         //this.el.show();
2249         this.hideMenuItems();
2250         this.hidden = false;
2251         this.triggerEl.addClass('open');
2252         
2253         // reassign x when hitting right
2254         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2256         }
2257         
2258         // reassign y when hitting bottom
2259         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2260             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2261         }
2262         
2263         // but the list may align on trigger left or trigger top... should it be a properity?
2264         
2265         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2266             this.el.setXY(xy);
2267         }
2268         
2269         this.focus();
2270         this.fireEvent("show", this);
2271     },
2272     
2273     focus : function(){
2274         return;
2275         if(!this.hidden){
2276             this.doFocus.defer(50, this);
2277         }
2278     },
2279
2280     doFocus : function(){
2281         if(!this.hidden){
2282             this.focusEl.focus();
2283         }
2284     },
2285
2286     /**
2287      * Hides this menu and optionally all parent menus
2288      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2289      */
2290     hide : function(deep)
2291     {
2292         
2293         this.hideMenuItems();
2294         if(this.el && this.isVisible()){
2295             this.fireEvent("beforehide", this);
2296             if(this.activeItem){
2297                 this.activeItem.deactivate();
2298                 this.activeItem = null;
2299             }
2300             this.triggerEl.removeClass('open');;
2301             this.hidden = true;
2302             this.fireEvent("hide", this);
2303         }
2304         if(deep === true && this.parentMenu){
2305             this.parentMenu.hide(true);
2306         }
2307     },
2308     
2309     onTriggerClick : function(e)
2310     {
2311         Roo.log('trigger click');
2312         
2313         var target = e.getTarget();
2314         
2315         Roo.log(target.nodeName.toLowerCase());
2316         
2317         if(target.nodeName.toLowerCase() === 'i'){
2318             e.preventDefault();
2319         }
2320         
2321     },
2322     
2323     onTriggerPress  : function(e)
2324     {
2325         Roo.log('trigger press');
2326         //Roo.log(e.getTarget());
2327        // Roo.log(this.triggerEl.dom);
2328        
2329         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330         var pel = Roo.get(e.getTarget());
2331         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332             Roo.log('is treeview or dropdown?');
2333             return;
2334         }
2335         
2336         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2337             return;
2338         }
2339         
2340         if (this.isVisible()) {
2341             Roo.log('hide');
2342             this.hide();
2343         } else {
2344             Roo.log('show');
2345             this.show(this.triggerEl, false, false);
2346         }
2347         
2348         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2349             e.stopEvent();
2350         }
2351         
2352     },
2353        
2354     
2355     hideMenuItems : function()
2356     {
2357         Roo.log("hide Menu Items");
2358         if (!this.el) { 
2359             return;
2360         }
2361         //$(backdrop).remove()
2362         this.el.select('.open',true).each(function(aa) {
2363             
2364             aa.removeClass('open');
2365           //var parent = getParent($(this))
2366           //var relatedTarget = { relatedTarget: this }
2367           
2368            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369           //if (e.isDefaultPrevented()) return
2370            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2371         });
2372     },
2373     addxtypeChild : function (tree, cntr) {
2374         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2375           
2376         this.menuitems.add(comp);
2377         return comp;
2378
2379     },
2380     getEl : function()
2381     {
2382         Roo.log(this.el);
2383         return this.el;
2384     },
2385     
2386     clear : function()
2387     {
2388         this.getEl().dom.innerHTML = '';
2389         this.menuitems.clear();
2390     }
2391 });
2392
2393  
2394  /*
2395  * - LGPL
2396  *
2397  * menu item
2398  * 
2399  */
2400
2401
2402 /**
2403  * @class Roo.bootstrap.MenuItem
2404  * @extends Roo.bootstrap.Component
2405  * Bootstrap MenuItem class
2406  * @cfg {String} html the menu label
2407  * @cfg {String} href the link
2408  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2411  * @cfg {String} fa favicon to show on left of menu item.
2412  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2413  * 
2414  * 
2415  * @constructor
2416  * Create a new MenuItem
2417  * @param {Object} config The config object
2418  */
2419
2420
2421 Roo.bootstrap.MenuItem = function(config){
2422     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2423     this.addEvents({
2424         // raw events
2425         /**
2426          * @event click
2427          * The raw click event for the entire grid.
2428          * @param {Roo.bootstrap.MenuItem} this
2429          * @param {Roo.EventObject} e
2430          */
2431         "click" : true
2432     });
2433 };
2434
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2436     
2437     href : false,
2438     html : false,
2439     preventDefault: false,
2440     isContainer : false,
2441     active : false,
2442     fa: false,
2443     
2444     getAutoCreate : function(){
2445         
2446         if(this.isContainer){
2447             return {
2448                 tag: 'li',
2449                 cls: 'dropdown-menu-item'
2450             };
2451         }
2452         var ctag = {
2453             tag: 'span',
2454             html: 'Link'
2455         };
2456         
2457         var anc = {
2458             tag : 'a',
2459             href : '#',
2460             cn : [  ]
2461         };
2462         
2463         if (this.fa !== false) {
2464             anc.cn.push({
2465                 tag : 'i',
2466                 cls : 'fa fa-' + this.fa
2467             });
2468         }
2469         
2470         anc.cn.push(ctag);
2471         
2472         
2473         var cfg= {
2474             tag: 'li',
2475             cls: 'dropdown-menu-item',
2476             cn: [ anc ]
2477         };
2478         if (this.parent().type == 'treeview') {
2479             cfg.cls = 'treeview-menu';
2480         }
2481         if (this.active) {
2482             cfg.cls += ' active';
2483         }
2484         
2485         
2486         
2487         anc.href = this.href || cfg.cn[0].href ;
2488         ctag.html = this.html || cfg.cn[0].html ;
2489         return cfg;
2490     },
2491     
2492     initEvents: function()
2493     {
2494         if (this.parent().type == 'treeview') {
2495             this.el.select('a').on('click', this.onClick, this);
2496         }
2497         
2498         if (this.menu) {
2499             this.menu.parentType = this.xtype;
2500             this.menu.triggerEl = this.el;
2501             this.menu = this.addxtype(Roo.apply({}, this.menu));
2502         }
2503         
2504     },
2505     onClick : function(e)
2506     {
2507         Roo.log('item on click ');
2508         
2509         if(this.preventDefault){
2510             e.preventDefault();
2511         }
2512         //this.parent().hideMenuItems();
2513         
2514         this.fireEvent('click', this, e);
2515     },
2516     getEl : function()
2517     {
2518         return this.el;
2519     } 
2520 });
2521
2522  
2523
2524  /*
2525  * - LGPL
2526  *
2527  * menu separator
2528  * 
2529  */
2530
2531
2532 /**
2533  * @class Roo.bootstrap.MenuSeparator
2534  * @extends Roo.bootstrap.Component
2535  * Bootstrap MenuSeparator class
2536  * 
2537  * @constructor
2538  * Create a new MenuItem
2539  * @param {Object} config The config object
2540  */
2541
2542
2543 Roo.bootstrap.MenuSeparator = function(config){
2544     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2545 };
2546
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2548     
2549     getAutoCreate : function(){
2550         var cfg = {
2551             cls: 'divider',
2552             tag : 'li'
2553         };
2554         
2555         return cfg;
2556     }
2557    
2558 });
2559
2560  
2561
2562  
2563 /*
2564 * Licence: LGPL
2565 */
2566
2567 /**
2568  * @class Roo.bootstrap.Modal
2569  * @extends Roo.bootstrap.Component
2570  * Bootstrap Modal class
2571  * @cfg {String} title Title of dialog
2572  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2574  * @cfg {Boolean} specificTitle default false
2575  * @cfg {Array} buttons Array of buttons or standard button set..
2576  * @cfg {String} buttonPosition (left|right|center) default right
2577  * @cfg {Boolean} animate default true
2578  * @cfg {Boolean} allow_close default true
2579  * @cfg {Boolean} fitwindow default false
2580  * @cfg {String} size (sm|lg) default empty
2581  * @cfg {Number} max_width set the max width of modal
2582  *
2583  *
2584  * @constructor
2585  * Create a new Modal Dialog
2586  * @param {Object} config The config object
2587  */
2588
2589 Roo.bootstrap.Modal = function(config){
2590     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2591     this.addEvents({
2592         // raw events
2593         /**
2594          * @event btnclick
2595          * The raw btnclick event for the button
2596          * @param {Roo.EventObject} e
2597          */
2598         "btnclick" : true,
2599         /**
2600          * @event resize
2601          * Fire when dialog resize
2602          * @param {Roo.bootstrap.Modal} this
2603          * @param {Roo.EventObject} e
2604          */
2605         "resize" : true
2606     });
2607     this.buttons = this.buttons || [];
2608
2609     if (this.tmpl) {
2610         this.tmpl = Roo.factory(this.tmpl);
2611     }
2612
2613 };
2614
2615 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2616
2617     title : 'test dialog',
2618
2619     buttons : false,
2620
2621     // set on load...
2622
2623     html: false,
2624
2625     tmp: false,
2626
2627     specificTitle: false,
2628
2629     buttonPosition: 'right',
2630
2631     allow_close : true,
2632
2633     animate : true,
2634
2635     fitwindow: false,
2636
2637
2638      // private
2639     dialogEl: false,
2640     bodyEl:  false,
2641     footerEl:  false,
2642     titleEl:  false,
2643     closeEl:  false,
2644
2645     size: '',
2646     
2647     max_width: 0,
2648
2649
2650     onRender : function(ct, position)
2651     {
2652         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2653
2654         if(!this.el){
2655             var cfg = Roo.apply({},  this.getAutoCreate());
2656             cfg.id = Roo.id();
2657             //if(!cfg.name){
2658             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2659             //}
2660             //if (!cfg.name.length) {
2661             //    delete cfg.name;
2662            // }
2663             if (this.cls) {
2664                 cfg.cls += ' ' + this.cls;
2665             }
2666             if (this.style) {
2667                 cfg.style = this.style;
2668             }
2669             this.el = Roo.get(document.body).createChild(cfg, position);
2670         }
2671         //var type = this.el.dom.type;
2672
2673
2674         if(this.tabIndex !== undefined){
2675             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2676         }
2677
2678         this.dialogEl = this.el.select('.modal-dialog',true).first();
2679         this.bodyEl = this.el.select('.modal-body',true).first();
2680         this.closeEl = this.el.select('.modal-header .close', true).first();
2681         this.headerEl = this.el.select('.modal-header',true).first();
2682         this.titleEl = this.el.select('.modal-title',true).first();
2683         this.footerEl = this.el.select('.modal-footer',true).first();
2684
2685         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2686         
2687         //this.el.addClass("x-dlg-modal");
2688
2689         if (this.buttons.length) {
2690             Roo.each(this.buttons, function(bb) {
2691                 var b = Roo.apply({}, bb);
2692                 b.xns = b.xns || Roo.bootstrap;
2693                 b.xtype = b.xtype || 'Button';
2694                 if (typeof(b.listeners) == 'undefined') {
2695                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2696                 }
2697
2698                 var btn = Roo.factory(b);
2699
2700                 btn.render(this.el.select('.modal-footer div').first());
2701
2702             },this);
2703         }
2704         // render the children.
2705         var nitems = [];
2706
2707         if(typeof(this.items) != 'undefined'){
2708             var items = this.items;
2709             delete this.items;
2710
2711             for(var i =0;i < items.length;i++) {
2712                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2713             }
2714         }
2715
2716         this.items = nitems;
2717
2718         // where are these used - they used to be body/close/footer
2719
2720
2721         this.initEvents();
2722         //this.el.addClass([this.fieldClass, this.cls]);
2723
2724     },
2725
2726     getAutoCreate : function()
2727     {
2728         var bdy = {
2729                 cls : 'modal-body',
2730                 html : this.html || ''
2731         };
2732
2733         var title = {
2734             tag: 'h4',
2735             cls : 'modal-title',
2736             html : this.title
2737         };
2738
2739         if(this.specificTitle){
2740             title = this.title;
2741
2742         };
2743
2744         var header = [];
2745         if (this.allow_close) {
2746             header.push({
2747                 tag: 'button',
2748                 cls : 'close',
2749                 html : '&times'
2750             });
2751         }
2752
2753         header.push(title);
2754
2755         var size = '';
2756
2757         if(this.size.length){
2758             size = 'modal-' + this.size;
2759         }
2760
2761         var modal = {
2762             cls: "modal",
2763              cn : [
2764                 {
2765                     cls: "modal-dialog " + size,
2766                     cn : [
2767                         {
2768                             cls : "modal-content",
2769                             cn : [
2770                                 {
2771                                     cls : 'modal-header',
2772                                     cn : header
2773                                 },
2774                                 bdy,
2775                                 {
2776                                     cls : 'modal-footer',
2777                                     cn : [
2778                                         {
2779                                             tag: 'div',
2780                                             cls: 'btn-' + this.buttonPosition
2781                                         }
2782                                     ]
2783
2784                                 }
2785
2786
2787                             ]
2788
2789                         }
2790                     ]
2791
2792                 }
2793             ]
2794         };
2795
2796         if(this.animate){
2797             modal.cls += ' fade';
2798         }
2799
2800         return modal;
2801
2802     },
2803     getChildContainer : function() {
2804
2805          return this.bodyEl;
2806
2807     },
2808     getButtonContainer : function() {
2809          return this.el.select('.modal-footer div',true).first();
2810
2811     },
2812     initEvents : function()
2813     {
2814         if (this.allow_close) {
2815             this.closeEl.on('click', this.hide, this);
2816         }
2817         Roo.EventManager.onWindowResize(this.resize, this, true);
2818
2819
2820     },
2821
2822     resize : function()
2823     {
2824         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2825         
2826         if (this.fitwindow) {
2827             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2828             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2829             this.setSize(w,h);
2830         }
2831         
2832         if(!this.fitwindow && this.max_width !== 0){
2833             
2834             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2835             
2836             // for fix height
2837             if(this.height) {
2838                 this.setSize(w, this.height);
2839                 return;
2840             }
2841             
2842             var body_childs = this.bodyEl.dom.childNodes;
2843             var full_height = this.headerEl.getHeight() + this.footerEl.getHeight();
2844             for(var i = 0; i < body_childs.length; i++) {
2845                 full_height += body_childs[i].offsetHeight;
2846             }
2847             
2848             this.setSize(w, Math.min(full_height, Roo.lib.Dom.getViewportHeight(true) - 60));
2849         }
2850         
2851     },
2852
2853     setSize : function(w,h)
2854     {
2855         if (!w && !h) {
2856             return;
2857         }
2858         this.resizeTo(w,h);
2859     },
2860
2861     show : function() {
2862
2863         if (!this.rendered) {
2864             this.render();
2865         }
2866
2867         //this.el.setStyle('display', 'block');
2868         this.el.removeClass('hideing');        
2869         this.el.addClass('show');
2870  
2871         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2872             var _this = this;
2873             (function(){
2874                 this.el.addClass('in');
2875             }).defer(50, this);
2876         }else{
2877             this.el.addClass('in');
2878         }
2879
2880         // not sure how we can show data in here..
2881         //if (this.tmpl) {
2882         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2883         //}
2884
2885         Roo.get(document.body).addClass("x-body-masked");
2886         
2887         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2888         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2889         this.maskEl.addClass('show');
2890         
2891         this.resize();
2892         
2893         this.fireEvent('show', this);
2894
2895         // set zindex here - otherwise it appears to be ignored...
2896         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2897
2898         (function () {
2899             this.items.forEach( function(e) {
2900                 e.layout ? e.layout() : false;
2901
2902             });
2903         }).defer(100,this);
2904
2905     },
2906     hide : function()
2907     {
2908         if(this.fireEvent("beforehide", this) !== false){
2909             this.maskEl.removeClass('show');
2910             Roo.get(document.body).removeClass("x-body-masked");
2911             this.el.removeClass('in');
2912             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2913
2914             if(this.animate){ // why
2915                 this.el.addClass('hideing');
2916                 (function(){
2917                     if (!this.el.hasClass('hideing')) {
2918                         return; // it's been shown again...
2919                     }
2920                     this.el.removeClass('show');
2921                     this.el.removeClass('hideing');
2922                 }).defer(150,this);
2923                 
2924             }else{
2925                  this.el.removeClass('show');
2926             }
2927             this.fireEvent('hide', this);
2928         }
2929     },
2930     isVisible : function()
2931     {
2932         
2933         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2934         
2935     },
2936
2937     addButton : function(str, cb)
2938     {
2939
2940
2941         var b = Roo.apply({}, { html : str } );
2942         b.xns = b.xns || Roo.bootstrap;
2943         b.xtype = b.xtype || 'Button';
2944         if (typeof(b.listeners) == 'undefined') {
2945             b.listeners = { click : cb.createDelegate(this)  };
2946         }
2947
2948         var btn = Roo.factory(b);
2949
2950         btn.render(this.el.select('.modal-footer div').first());
2951
2952         return btn;
2953
2954     },
2955
2956     setDefaultButton : function(btn)
2957     {
2958         //this.el.select('.modal-footer').()
2959     },
2960     diff : false,
2961
2962     resizeTo: function(w,h)
2963     {
2964         // skip.. ?? why??
2965
2966         this.dialogEl.setWidth(w);
2967         if (this.diff === false) {
2968             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2969         }
2970
2971         this.bodyEl.setHeight(h-this.diff);
2972
2973         this.fireEvent('resize', this);
2974
2975     },
2976     setContentSize  : function(w, h)
2977     {
2978
2979     },
2980     onButtonClick: function(btn,e)
2981     {
2982         //Roo.log([a,b,c]);
2983         this.fireEvent('btnclick', btn.name, e);
2984     },
2985      /**
2986      * Set the title of the Dialog
2987      * @param {String} str new Title
2988      */
2989     setTitle: function(str) {
2990         this.titleEl.dom.innerHTML = str;
2991     },
2992     /**
2993      * Set the body of the Dialog
2994      * @param {String} str new Title
2995      */
2996     setBody: function(str) {
2997         this.bodyEl.dom.innerHTML = str;
2998     },
2999     /**
3000      * Set the body of the Dialog using the template
3001      * @param {Obj} data - apply this data to the template and replace the body contents.
3002      */
3003     applyBody: function(obj)
3004     {
3005         if (!this.tmpl) {
3006             Roo.log("Error - using apply Body without a template");
3007             //code
3008         }
3009         this.tmpl.overwrite(this.bodyEl, obj);
3010     }
3011
3012 });
3013
3014
3015 Roo.apply(Roo.bootstrap.Modal,  {
3016     /**
3017          * Button config that displays a single OK button
3018          * @type Object
3019          */
3020         OK :  [{
3021             name : 'ok',
3022             weight : 'primary',
3023             html : 'OK'
3024         }],
3025         /**
3026          * Button config that displays Yes and No buttons
3027          * @type Object
3028          */
3029         YESNO : [
3030             {
3031                 name  : 'no',
3032                 html : 'No'
3033             },
3034             {
3035                 name  :'yes',
3036                 weight : 'primary',
3037                 html : 'Yes'
3038             }
3039         ],
3040
3041         /**
3042          * Button config that displays OK and Cancel buttons
3043          * @type Object
3044          */
3045         OKCANCEL : [
3046             {
3047                name : 'cancel',
3048                 html : 'Cancel'
3049             },
3050             {
3051                 name : 'ok',
3052                 weight : 'primary',
3053                 html : 'OK'
3054             }
3055         ],
3056         /**
3057          * Button config that displays Yes, No and Cancel buttons
3058          * @type Object
3059          */
3060         YESNOCANCEL : [
3061             {
3062                 name : 'yes',
3063                 weight : 'primary',
3064                 html : 'Yes'
3065             },
3066             {
3067                 name : 'no',
3068                 html : 'No'
3069             },
3070             {
3071                 name : 'cancel',
3072                 html : 'Cancel'
3073             }
3074         ],
3075         
3076         zIndex : 10001
3077 });
3078 /*
3079  * - LGPL
3080  *
3081  * messagebox - can be used as a replace
3082  * 
3083  */
3084 /**
3085  * @class Roo.MessageBox
3086  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3087  * Example usage:
3088  *<pre><code>
3089 // Basic alert:
3090 Roo.Msg.alert('Status', 'Changes saved successfully.');
3091
3092 // Prompt for user data:
3093 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3094     if (btn == 'ok'){
3095         // process text value...
3096     }
3097 });
3098
3099 // Show a dialog using config options:
3100 Roo.Msg.show({
3101    title:'Save Changes?',
3102    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3103    buttons: Roo.Msg.YESNOCANCEL,
3104    fn: processResult,
3105    animEl: 'elId'
3106 });
3107 </code></pre>
3108  * @singleton
3109  */
3110 Roo.bootstrap.MessageBox = function(){
3111     var dlg, opt, mask, waitTimer;
3112     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3113     var buttons, activeTextEl, bwidth;
3114
3115     
3116     // private
3117     var handleButton = function(button){
3118         dlg.hide();
3119         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3120     };
3121
3122     // private
3123     var handleHide = function(){
3124         if(opt && opt.cls){
3125             dlg.el.removeClass(opt.cls);
3126         }
3127         //if(waitTimer){
3128         //    Roo.TaskMgr.stop(waitTimer);
3129         //    waitTimer = null;
3130         //}
3131     };
3132
3133     // private
3134     var updateButtons = function(b){
3135         var width = 0;
3136         if(!b){
3137             buttons["ok"].hide();
3138             buttons["cancel"].hide();
3139             buttons["yes"].hide();
3140             buttons["no"].hide();
3141             //dlg.footer.dom.style.display = 'none';
3142             return width;
3143         }
3144         dlg.footerEl.dom.style.display = '';
3145         for(var k in buttons){
3146             if(typeof buttons[k] != "function"){
3147                 if(b[k]){
3148                     buttons[k].show();
3149                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3150                     width += buttons[k].el.getWidth()+15;
3151                 }else{
3152                     buttons[k].hide();
3153                 }
3154             }
3155         }
3156         return width;
3157     };
3158
3159     // private
3160     var handleEsc = function(d, k, e){
3161         if(opt && opt.closable !== false){
3162             dlg.hide();
3163         }
3164         if(e){
3165             e.stopEvent();
3166         }
3167     };
3168
3169     return {
3170         /**
3171          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3172          * @return {Roo.BasicDialog} The BasicDialog element
3173          */
3174         getDialog : function(){
3175            if(!dlg){
3176                 dlg = new Roo.bootstrap.Modal( {
3177                     //draggable: true,
3178                     //resizable:false,
3179                     //constraintoviewport:false,
3180                     //fixedcenter:true,
3181                     //collapsible : false,
3182                     //shim:true,
3183                     //modal: true,
3184                 //    width: 'auto',
3185                   //  height:100,
3186                     //buttonAlign:"center",
3187                     closeClick : function(){
3188                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3189                             handleButton("no");
3190                         }else{
3191                             handleButton("cancel");
3192                         }
3193                     }
3194                 });
3195                 dlg.render();
3196                 dlg.on("hide", handleHide);
3197                 mask = dlg.mask;
3198                 //dlg.addKeyListener(27, handleEsc);
3199                 buttons = {};
3200                 this.buttons = buttons;
3201                 var bt = this.buttonText;
3202                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3203                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3204                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3205                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3206                 //Roo.log(buttons);
3207                 bodyEl = dlg.bodyEl.createChild({
3208
3209                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3210                         '<textarea class="roo-mb-textarea"></textarea>' +
3211                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3212                 });
3213                 msgEl = bodyEl.dom.firstChild;
3214                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3215                 textboxEl.enableDisplayMode();
3216                 textboxEl.addKeyListener([10,13], function(){
3217                     if(dlg.isVisible() && opt && opt.buttons){
3218                         if(opt.buttons.ok){
3219                             handleButton("ok");
3220                         }else if(opt.buttons.yes){
3221                             handleButton("yes");
3222                         }
3223                     }
3224                 });
3225                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3226                 textareaEl.enableDisplayMode();
3227                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3228                 progressEl.enableDisplayMode();
3229                 
3230                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3231                 var pf = progressEl.dom.firstChild;
3232                 if (pf) {
3233                     pp = Roo.get(pf.firstChild);
3234                     pp.setHeight(pf.offsetHeight);
3235                 }
3236                 
3237             }
3238             return dlg;
3239         },
3240
3241         /**
3242          * Updates the message box body text
3243          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3244          * the XHTML-compliant non-breaking space character '&amp;#160;')
3245          * @return {Roo.MessageBox} This message box
3246          */
3247         updateText : function(text)
3248         {
3249             if(!dlg.isVisible() && !opt.width){
3250                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3251                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3252             }
3253             msgEl.innerHTML = text || '&#160;';
3254       
3255             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3256             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3257             var w = Math.max(
3258                     Math.min(opt.width || cw , this.maxWidth), 
3259                     Math.max(opt.minWidth || this.minWidth, bwidth)
3260             );
3261             if(opt.prompt){
3262                 activeTextEl.setWidth(w);
3263             }
3264             if(dlg.isVisible()){
3265                 dlg.fixedcenter = false;
3266             }
3267             // to big, make it scroll. = But as usual stupid IE does not support
3268             // !important..
3269             
3270             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3271                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3272                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3273             } else {
3274                 bodyEl.dom.style.height = '';
3275                 bodyEl.dom.style.overflowY = '';
3276             }
3277             if (cw > w) {
3278                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3279             } else {
3280                 bodyEl.dom.style.overflowX = '';
3281             }
3282             
3283             dlg.setContentSize(w, bodyEl.getHeight());
3284             if(dlg.isVisible()){
3285                 dlg.fixedcenter = true;
3286             }
3287             return this;
3288         },
3289
3290         /**
3291          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3292          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3293          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3294          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3295          * @return {Roo.MessageBox} This message box
3296          */
3297         updateProgress : function(value, text){
3298             if(text){
3299                 this.updateText(text);
3300             }
3301             
3302             if (pp) { // weird bug on my firefox - for some reason this is not defined
3303                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3304                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3305             }
3306             return this;
3307         },        
3308
3309         /**
3310          * Returns true if the message box is currently displayed
3311          * @return {Boolean} True if the message box is visible, else false
3312          */
3313         isVisible : function(){
3314             return dlg && dlg.isVisible();  
3315         },
3316
3317         /**
3318          * Hides the message box if it is displayed
3319          */
3320         hide : function(){
3321             if(this.isVisible()){
3322                 dlg.hide();
3323             }  
3324         },
3325
3326         /**
3327          * Displays a new message box, or reinitializes an existing message box, based on the config options
3328          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3329          * The following config object properties are supported:
3330          * <pre>
3331 Property    Type             Description
3332 ----------  ---------------  ------------------------------------------------------------------------------------
3333 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3334                                    closes (defaults to undefined)
3335 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3336                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3337 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3338                                    progress and wait dialogs will ignore this property and always hide the
3339                                    close button as they can only be closed programmatically.
3340 cls               String           A custom CSS class to apply to the message box element
3341 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3342                                    displayed (defaults to 75)
3343 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3344                                    function will be btn (the name of the button that was clicked, if applicable,
3345                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3346                                    Progress and wait dialogs will ignore this option since they do not respond to
3347                                    user actions and can only be closed programmatically, so any required function
3348                                    should be called by the same code after it closes the dialog.
3349 icon              String           A CSS class that provides a background image to be used as an icon for
3350                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3351 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3352 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3353 modal             Boolean          False to allow user interaction with the page while the message box is
3354                                    displayed (defaults to true)
3355 msg               String           A string that will replace the existing message box body text (defaults
3356                                    to the XHTML-compliant non-breaking space character '&#160;')
3357 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3358 progress          Boolean          True to display a progress bar (defaults to false)
3359 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3360 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3361 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3362 title             String           The title text
3363 value             String           The string value to set into the active textbox element if displayed
3364 wait              Boolean          True to display a progress bar (defaults to false)
3365 width             Number           The width of the dialog in pixels
3366 </pre>
3367          *
3368          * Example usage:
3369          * <pre><code>
3370 Roo.Msg.show({
3371    title: 'Address',
3372    msg: 'Please enter your address:',
3373    width: 300,
3374    buttons: Roo.MessageBox.OKCANCEL,
3375    multiline: true,
3376    fn: saveAddress,
3377    animEl: 'addAddressBtn'
3378 });
3379 </code></pre>
3380          * @param {Object} config Configuration options
3381          * @return {Roo.MessageBox} This message box
3382          */
3383         show : function(options)
3384         {
3385             
3386             // this causes nightmares if you show one dialog after another
3387             // especially on callbacks..
3388              
3389             if(this.isVisible()){
3390                 
3391                 this.hide();
3392                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3393                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3394                 Roo.log("New Dialog Message:" +  options.msg )
3395                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3396                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3397                 
3398             }
3399             var d = this.getDialog();
3400             opt = options;
3401             d.setTitle(opt.title || "&#160;");
3402             d.closeEl.setDisplayed(opt.closable !== false);
3403             activeTextEl = textboxEl;
3404             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3405             if(opt.prompt){
3406                 if(opt.multiline){
3407                     textboxEl.hide();
3408                     textareaEl.show();
3409                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3410                         opt.multiline : this.defaultTextHeight);
3411                     activeTextEl = textareaEl;
3412                 }else{
3413                     textboxEl.show();
3414                     textareaEl.hide();
3415                 }
3416             }else{
3417                 textboxEl.hide();
3418                 textareaEl.hide();
3419             }
3420             progressEl.setDisplayed(opt.progress === true);
3421             this.updateProgress(0);
3422             activeTextEl.dom.value = opt.value || "";
3423             if(opt.prompt){
3424                 dlg.setDefaultButton(activeTextEl);
3425             }else{
3426                 var bs = opt.buttons;
3427                 var db = null;
3428                 if(bs && bs.ok){
3429                     db = buttons["ok"];
3430                 }else if(bs && bs.yes){
3431                     db = buttons["yes"];
3432                 }
3433                 dlg.setDefaultButton(db);
3434             }
3435             bwidth = updateButtons(opt.buttons);
3436             this.updateText(opt.msg);
3437             if(opt.cls){
3438                 d.el.addClass(opt.cls);
3439             }
3440             d.proxyDrag = opt.proxyDrag === true;
3441             d.modal = opt.modal !== false;
3442             d.mask = opt.modal !== false ? mask : false;
3443             if(!d.isVisible()){
3444                 // force it to the end of the z-index stack so it gets a cursor in FF
3445                 document.body.appendChild(dlg.el.dom);
3446                 d.animateTarget = null;
3447                 d.show(options.animEl);
3448             }
3449             return this;
3450         },
3451
3452         /**
3453          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3454          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3455          * and closing the message box when the process is complete.
3456          * @param {String} title The title bar text
3457          * @param {String} msg The message box body text
3458          * @return {Roo.MessageBox} This message box
3459          */
3460         progress : function(title, msg){
3461             this.show({
3462                 title : title,
3463                 msg : msg,
3464                 buttons: false,
3465                 progress:true,
3466                 closable:false,
3467                 minWidth: this.minProgressWidth,
3468                 modal : true
3469             });
3470             return this;
3471         },
3472
3473         /**
3474          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3475          * If a callback function is passed it will be called after the user clicks the button, and the
3476          * id of the button that was clicked will be passed as the only parameter to the callback
3477          * (could also be the top-right close button).
3478          * @param {String} title The title bar text
3479          * @param {String} msg The message box body text
3480          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3481          * @param {Object} scope (optional) The scope of the callback function
3482          * @return {Roo.MessageBox} This message box
3483          */
3484         alert : function(title, msg, fn, scope)
3485         {
3486             this.show({
3487                 title : title,
3488                 msg : msg,
3489                 buttons: this.OK,
3490                 fn: fn,
3491                 closable : false,
3492                 scope : scope,
3493                 modal : true
3494             });
3495             return this;
3496         },
3497
3498         /**
3499          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3500          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3501          * You are responsible for closing the message box when the process is complete.
3502          * @param {String} msg The message box body text
3503          * @param {String} title (optional) The title bar text
3504          * @return {Roo.MessageBox} This message box
3505          */
3506         wait : function(msg, title){
3507             this.show({
3508                 title : title,
3509                 msg : msg,
3510                 buttons: false,
3511                 closable:false,
3512                 progress:true,
3513                 modal:true,
3514                 width:300,
3515                 wait:true
3516             });
3517             waitTimer = Roo.TaskMgr.start({
3518                 run: function(i){
3519                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3520                 },
3521                 interval: 1000
3522             });
3523             return this;
3524         },
3525
3526         /**
3527          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3528          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3529          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3530          * @param {String} title The title bar text
3531          * @param {String} msg The message box body text
3532          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3533          * @param {Object} scope (optional) The scope of the callback function
3534          * @return {Roo.MessageBox} This message box
3535          */
3536         confirm : function(title, msg, fn, scope){
3537             this.show({
3538                 title : title,
3539                 msg : msg,
3540                 buttons: this.YESNO,
3541                 fn: fn,
3542                 scope : scope,
3543                 modal : true
3544             });
3545             return this;
3546         },
3547
3548         /**
3549          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3550          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3551          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3552          * (could also be the top-right close button) and the text that was entered will be passed as the two
3553          * parameters to the callback.
3554          * @param {String} title The title bar text
3555          * @param {String} msg The message box body text
3556          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3557          * @param {Object} scope (optional) The scope of the callback function
3558          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3559          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3560          * @return {Roo.MessageBox} This message box
3561          */
3562         prompt : function(title, msg, fn, scope, multiline){
3563             this.show({
3564                 title : title,
3565                 msg : msg,
3566                 buttons: this.OKCANCEL,
3567                 fn: fn,
3568                 minWidth:250,
3569                 scope : scope,
3570                 prompt:true,
3571                 multiline: multiline,
3572                 modal : true
3573             });
3574             return this;
3575         },
3576
3577         /**
3578          * Button config that displays a single OK button
3579          * @type Object
3580          */
3581         OK : {ok:true},
3582         /**
3583          * Button config that displays Yes and No buttons
3584          * @type Object
3585          */
3586         YESNO : {yes:true, no:true},
3587         /**
3588          * Button config that displays OK and Cancel buttons
3589          * @type Object
3590          */
3591         OKCANCEL : {ok:true, cancel:true},
3592         /**
3593          * Button config that displays Yes, No and Cancel buttons
3594          * @type Object
3595          */
3596         YESNOCANCEL : {yes:true, no:true, cancel:true},
3597
3598         /**
3599          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3600          * @type Number
3601          */
3602         defaultTextHeight : 75,
3603         /**
3604          * The maximum width in pixels of the message box (defaults to 600)
3605          * @type Number
3606          */
3607         maxWidth : 600,
3608         /**
3609          * The minimum width in pixels of the message box (defaults to 100)
3610          * @type Number
3611          */
3612         minWidth : 100,
3613         /**
3614          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3615          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3616          * @type Number
3617          */
3618         minProgressWidth : 250,
3619         /**
3620          * An object containing the default button text strings that can be overriden for localized language support.
3621          * Supported properties are: ok, cancel, yes and no.
3622          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3623          * @type Object
3624          */
3625         buttonText : {
3626             ok : "OK",
3627             cancel : "Cancel",
3628             yes : "Yes",
3629             no : "No"
3630         }
3631     };
3632 }();
3633
3634 /**
3635  * Shorthand for {@link Roo.MessageBox}
3636  */
3637 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3638 Roo.Msg = Roo.Msg || Roo.MessageBox;
3639 /*
3640  * - LGPL
3641  *
3642  * navbar
3643  * 
3644  */
3645
3646 /**
3647  * @class Roo.bootstrap.Navbar
3648  * @extends Roo.bootstrap.Component
3649  * Bootstrap Navbar class
3650
3651  * @constructor
3652  * Create a new Navbar
3653  * @param {Object} config The config object
3654  */
3655
3656
3657 Roo.bootstrap.Navbar = function(config){
3658     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3659     this.addEvents({
3660         // raw events
3661         /**
3662          * @event beforetoggle
3663          * Fire before toggle the menu
3664          * @param {Roo.EventObject} e
3665          */
3666         "beforetoggle" : true
3667     });
3668 };
3669
3670 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3671     
3672     
3673    
3674     // private
3675     navItems : false,
3676     loadMask : false,
3677     
3678     
3679     getAutoCreate : function(){
3680         
3681         
3682         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3683         
3684     },
3685     
3686     initEvents :function ()
3687     {
3688         //Roo.log(this.el.select('.navbar-toggle',true));
3689         this.el.select('.navbar-toggle',true).on('click', function() {
3690             if(this.fireEvent('beforetoggle', this) !== false){
3691                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3692             }
3693             
3694         }, this);
3695         
3696         var mark = {
3697             tag: "div",
3698             cls:"x-dlg-mask"
3699         };
3700         
3701         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3702         
3703         var size = this.el.getSize();
3704         this.maskEl.setSize(size.width, size.height);
3705         this.maskEl.enableDisplayMode("block");
3706         this.maskEl.hide();
3707         
3708         if(this.loadMask){
3709             this.maskEl.show();
3710         }
3711     },
3712     
3713     
3714     getChildContainer : function()
3715     {
3716         if (this.el.select('.collapse').getCount()) {
3717             return this.el.select('.collapse',true).first();
3718         }
3719         
3720         return this.el;
3721     },
3722     
3723     mask : function()
3724     {
3725         this.maskEl.show();
3726     },
3727     
3728     unmask : function()
3729     {
3730         this.maskEl.hide();
3731     } 
3732     
3733     
3734     
3735     
3736 });
3737
3738
3739
3740  
3741
3742  /*
3743  * - LGPL
3744  *
3745  * navbar
3746  * 
3747  */
3748
3749 /**
3750  * @class Roo.bootstrap.NavSimplebar
3751  * @extends Roo.bootstrap.Navbar
3752  * Bootstrap Sidebar class
3753  *
3754  * @cfg {Boolean} inverse is inverted color
3755  * 
3756  * @cfg {String} type (nav | pills | tabs)
3757  * @cfg {Boolean} arrangement stacked | justified
3758  * @cfg {String} align (left | right) alignment
3759  * 
3760  * @cfg {Boolean} main (true|false) main nav bar? default false
3761  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3762  * 
3763  * @cfg {String} tag (header|footer|nav|div) default is nav 
3764
3765  * 
3766  * 
3767  * 
3768  * @constructor
3769  * Create a new Sidebar
3770  * @param {Object} config The config object
3771  */
3772
3773
3774 Roo.bootstrap.NavSimplebar = function(config){
3775     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3776 };
3777
3778 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3779     
3780     inverse: false,
3781     
3782     type: false,
3783     arrangement: '',
3784     align : false,
3785     
3786     
3787     
3788     main : false,
3789     
3790     
3791     tag : false,
3792     
3793     
3794     getAutoCreate : function(){
3795         
3796         
3797         var cfg = {
3798             tag : this.tag || 'div',
3799             cls : 'navbar'
3800         };
3801           
3802         
3803         cfg.cn = [
3804             {
3805                 cls: 'nav',
3806                 tag : 'ul'
3807             }
3808         ];
3809         
3810          
3811         this.type = this.type || 'nav';
3812         if (['tabs','pills'].indexOf(this.type)!==-1) {
3813             cfg.cn[0].cls += ' nav-' + this.type
3814         
3815         
3816         } else {
3817             if (this.type!=='nav') {
3818                 Roo.log('nav type must be nav/tabs/pills')
3819             }
3820             cfg.cn[0].cls += ' navbar-nav'
3821         }
3822         
3823         
3824         
3825         
3826         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3827             cfg.cn[0].cls += ' nav-' + this.arrangement;
3828         }
3829         
3830         
3831         if (this.align === 'right') {
3832             cfg.cn[0].cls += ' navbar-right';
3833         }
3834         
3835         if (this.inverse) {
3836             cfg.cls += ' navbar-inverse';
3837             
3838         }
3839         
3840         
3841         return cfg;
3842     
3843         
3844     }
3845     
3846     
3847     
3848 });
3849
3850
3851
3852  
3853
3854  
3855        /*
3856  * - LGPL
3857  *
3858  * navbar
3859  * 
3860  */
3861
3862 /**
3863  * @class Roo.bootstrap.NavHeaderbar
3864  * @extends Roo.bootstrap.NavSimplebar
3865  * Bootstrap Sidebar class
3866  *
3867  * @cfg {String} brand what is brand
3868  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3869  * @cfg {String} brand_href href of the brand
3870  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3871  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3872  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3873  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3874  * 
3875  * @constructor
3876  * Create a new Sidebar
3877  * @param {Object} config The config object
3878  */
3879
3880
3881 Roo.bootstrap.NavHeaderbar = function(config){
3882     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3883       
3884 };
3885
3886 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3887     
3888     position: '',
3889     brand: '',
3890     brand_href: false,
3891     srButton : true,
3892     autohide : false,
3893     desktopCenter : false,
3894    
3895     
3896     getAutoCreate : function(){
3897         
3898         var   cfg = {
3899             tag: this.nav || 'nav',
3900             cls: 'navbar',
3901             role: 'navigation',
3902             cn: []
3903         };
3904         
3905         var cn = cfg.cn;
3906         if (this.desktopCenter) {
3907             cn.push({cls : 'container', cn : []});
3908             cn = cn[0].cn;
3909         }
3910         
3911         if(this.srButton){
3912             cn.push({
3913                 tag: 'div',
3914                 cls: 'navbar-header',
3915                 cn: [
3916                     {
3917                         tag: 'button',
3918                         type: 'button',
3919                         cls: 'navbar-toggle',
3920                         'data-toggle': 'collapse',
3921                         cn: [
3922                             {
3923                                 tag: 'span',
3924                                 cls: 'sr-only',
3925                                 html: 'Toggle navigation'
3926                             },
3927                             {
3928                                 tag: 'span',
3929                                 cls: 'icon-bar'
3930                             },
3931                             {
3932                                 tag: 'span',
3933                                 cls: 'icon-bar'
3934                             },
3935                             {
3936                                 tag: 'span',
3937                                 cls: 'icon-bar'
3938                             }
3939                         ]
3940                     }
3941                 ]
3942             });
3943         }
3944         
3945         cn.push({
3946             tag: 'div',
3947             cls: 'collapse navbar-collapse',
3948             cn : []
3949         });
3950         
3951         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3952         
3953         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3954             cfg.cls += ' navbar-' + this.position;
3955             
3956             // tag can override this..
3957             
3958             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3959         }
3960         
3961         if (this.brand !== '') {
3962             cn[0].cn.push({
3963                 tag: 'a',
3964                 href: this.brand_href ? this.brand_href : '#',
3965                 cls: 'navbar-brand',
3966                 cn: [
3967                 this.brand
3968                 ]
3969             });
3970         }
3971         
3972         if(this.main){
3973             cfg.cls += ' main-nav';
3974         }
3975         
3976         
3977         return cfg;
3978
3979         
3980     },
3981     getHeaderChildContainer : function()
3982     {
3983         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3984             return this.el.select('.navbar-header',true).first();
3985         }
3986         
3987         return this.getChildContainer();
3988     },
3989     
3990     
3991     initEvents : function()
3992     {
3993         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3994         
3995         if (this.autohide) {
3996             
3997             var prevScroll = 0;
3998             var ft = this.el;
3999             
4000             Roo.get(document).on('scroll',function(e) {
4001                 var ns = Roo.get(document).getScroll().top;
4002                 var os = prevScroll;
4003                 prevScroll = ns;
4004                 
4005                 if(ns > os){
4006                     ft.removeClass('slideDown');
4007                     ft.addClass('slideUp');
4008                     return;
4009                 }
4010                 ft.removeClass('slideUp');
4011                 ft.addClass('slideDown');
4012                  
4013               
4014           },this);
4015         }
4016     }    
4017     
4018 });
4019
4020
4021
4022  
4023
4024  /*
4025  * - LGPL
4026  *
4027  * navbar
4028  * 
4029  */
4030
4031 /**
4032  * @class Roo.bootstrap.NavSidebar
4033  * @extends Roo.bootstrap.Navbar
4034  * Bootstrap Sidebar class
4035  * 
4036  * @constructor
4037  * Create a new Sidebar
4038  * @param {Object} config The config object
4039  */
4040
4041
4042 Roo.bootstrap.NavSidebar = function(config){
4043     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4044 };
4045
4046 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4047     
4048     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4049     
4050     getAutoCreate : function(){
4051         
4052         
4053         return  {
4054             tag: 'div',
4055             cls: 'sidebar sidebar-nav'
4056         };
4057     
4058         
4059     }
4060     
4061     
4062     
4063 });
4064
4065
4066
4067  
4068
4069  /*
4070  * - LGPL
4071  *
4072  * nav group
4073  * 
4074  */
4075
4076 /**
4077  * @class Roo.bootstrap.NavGroup
4078  * @extends Roo.bootstrap.Component
4079  * Bootstrap NavGroup class
4080  * @cfg {String} align (left|right)
4081  * @cfg {Boolean} inverse
4082  * @cfg {String} type (nav|pills|tab) default nav
4083  * @cfg {String} navId - reference Id for navbar.
4084
4085  * 
4086  * @constructor
4087  * Create a new nav group
4088  * @param {Object} config The config object
4089  */
4090
4091 Roo.bootstrap.NavGroup = function(config){
4092     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4093     this.navItems = [];
4094    
4095     Roo.bootstrap.NavGroup.register(this);
4096      this.addEvents({
4097         /**
4098              * @event changed
4099              * Fires when the active item changes
4100              * @param {Roo.bootstrap.NavGroup} this
4101              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4102              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4103          */
4104         'changed': true
4105      });
4106     
4107 };
4108
4109 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4110     
4111     align: '',
4112     inverse: false,
4113     form: false,
4114     type: 'nav',
4115     navId : '',
4116     // private
4117     
4118     navItems : false, 
4119     
4120     getAutoCreate : function()
4121     {
4122         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4123         
4124         cfg = {
4125             tag : 'ul',
4126             cls: 'nav' 
4127         };
4128         
4129         if (['tabs','pills'].indexOf(this.type)!==-1) {
4130             cfg.cls += ' nav-' + this.type
4131         } else {
4132             if (this.type!=='nav') {
4133                 Roo.log('nav type must be nav/tabs/pills')
4134             }
4135             cfg.cls += ' navbar-nav'
4136         }
4137         
4138         if (this.parent() && this.parent().sidebar) {
4139             cfg = {
4140                 tag: 'ul',
4141                 cls: 'dashboard-menu sidebar-menu'
4142             };
4143             
4144             return cfg;
4145         }
4146         
4147         if (this.form === true) {
4148             cfg = {
4149                 tag: 'form',
4150                 cls: 'navbar-form'
4151             };
4152             
4153             if (this.align === 'right') {
4154                 cfg.cls += ' navbar-right';
4155             } else {
4156                 cfg.cls += ' navbar-left';
4157             }
4158         }
4159         
4160         if (this.align === 'right') {
4161             cfg.cls += ' navbar-right';
4162         }
4163         
4164         if (this.inverse) {
4165             cfg.cls += ' navbar-inverse';
4166             
4167         }
4168         
4169         
4170         return cfg;
4171     },
4172     /**
4173     * sets the active Navigation item
4174     * @param {Roo.bootstrap.NavItem} the new current navitem
4175     */
4176     setActiveItem : function(item)
4177     {
4178         var prev = false;
4179         Roo.each(this.navItems, function(v){
4180             if (v == item) {
4181                 return ;
4182             }
4183             if (v.isActive()) {
4184                 v.setActive(false, true);
4185                 prev = v;
4186                 
4187             }
4188             
4189         });
4190
4191         item.setActive(true, true);
4192         this.fireEvent('changed', this, item, prev);
4193         
4194         
4195     },
4196     /**
4197     * gets the active Navigation item
4198     * @return {Roo.bootstrap.NavItem} the current navitem
4199     */
4200     getActive : function()
4201     {
4202         
4203         var prev = false;
4204         Roo.each(this.navItems, function(v){
4205             
4206             if (v.isActive()) {
4207                 prev = v;
4208                 
4209             }
4210             
4211         });
4212         return prev;
4213     },
4214     
4215     indexOfNav : function()
4216     {
4217         
4218         var prev = false;
4219         Roo.each(this.navItems, function(v,i){
4220             
4221             if (v.isActive()) {
4222                 prev = i;
4223                 
4224             }
4225             
4226         });
4227         return prev;
4228     },
4229     /**
4230     * adds a Navigation item
4231     * @param {Roo.bootstrap.NavItem} the navitem to add
4232     */
4233     addItem : function(cfg)
4234     {
4235         var cn = new Roo.bootstrap.NavItem(cfg);
4236         this.register(cn);
4237         cn.parentId = this.id;
4238         cn.onRender(this.el, null);
4239         return cn;
4240     },
4241     /**
4242     * register a Navigation item
4243     * @param {Roo.bootstrap.NavItem} the navitem to add
4244     */
4245     register : function(item)
4246     {
4247         this.navItems.push( item);
4248         item.navId = this.navId;
4249     
4250     },
4251     
4252     /**
4253     * clear all the Navigation item
4254     */
4255    
4256     clearAll : function()
4257     {
4258         this.navItems = [];
4259         this.el.dom.innerHTML = '';
4260     },
4261     
4262     getNavItem: function(tabId)
4263     {
4264         var ret = false;
4265         Roo.each(this.navItems, function(e) {
4266             if (e.tabId == tabId) {
4267                ret =  e;
4268                return false;
4269             }
4270             return true;
4271             
4272         });
4273         return ret;
4274     },
4275     
4276     setActiveNext : function()
4277     {
4278         var i = this.indexOfNav(this.getActive());
4279         if (i > this.navItems.length) {
4280             return;
4281         }
4282         this.setActiveItem(this.navItems[i+1]);
4283     },
4284     setActivePrev : function()
4285     {
4286         var i = this.indexOfNav(this.getActive());
4287         if (i  < 1) {
4288             return;
4289         }
4290         this.setActiveItem(this.navItems[i-1]);
4291     },
4292     clearWasActive : function(except) {
4293         Roo.each(this.navItems, function(e) {
4294             if (e.tabId != except.tabId && e.was_active) {
4295                e.was_active = false;
4296                return false;
4297             }
4298             return true;
4299             
4300         });
4301     },
4302     getWasActive : function ()
4303     {
4304         var r = false;
4305         Roo.each(this.navItems, function(e) {
4306             if (e.was_active) {
4307                r = e;
4308                return false;
4309             }
4310             return true;
4311             
4312         });
4313         return r;
4314     }
4315     
4316     
4317 });
4318
4319  
4320 Roo.apply(Roo.bootstrap.NavGroup, {
4321     
4322     groups: {},
4323      /**
4324     * register a Navigation Group
4325     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4326     */
4327     register : function(navgrp)
4328     {
4329         this.groups[navgrp.navId] = navgrp;
4330         
4331     },
4332     /**
4333     * fetch a Navigation Group based on the navigation ID
4334     * @param {string} the navgroup to add
4335     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4336     */
4337     get: function(navId) {
4338         if (typeof(this.groups[navId]) == 'undefined') {
4339             return false;
4340             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4341         }
4342         return this.groups[navId] ;
4343     }
4344     
4345     
4346     
4347 });
4348
4349  /*
4350  * - LGPL
4351  *
4352  * row
4353  * 
4354  */
4355
4356 /**
4357  * @class Roo.bootstrap.NavItem
4358  * @extends Roo.bootstrap.Component
4359  * Bootstrap Navbar.NavItem class
4360  * @cfg {String} href  link to
4361  * @cfg {String} html content of button
4362  * @cfg {String} badge text inside badge
4363  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4364  * @cfg {String} glyphicon name of glyphicon
4365  * @cfg {String} icon name of font awesome icon
4366  * @cfg {Boolean} active Is item active
4367  * @cfg {Boolean} disabled Is item disabled
4368  
4369  * @cfg {Boolean} preventDefault (true | false) default false
4370  * @cfg {String} tabId the tab that this item activates.
4371  * @cfg {String} tagtype (a|span) render as a href or span?
4372  * @cfg {Boolean} animateRef (true|false) link to element default false  
4373   
4374  * @constructor
4375  * Create a new Navbar Item
4376  * @param {Object} config The config object
4377  */
4378 Roo.bootstrap.NavItem = function(config){
4379     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4380     this.addEvents({
4381         // raw events
4382         /**
4383          * @event click
4384          * The raw click event for the entire grid.
4385          * @param {Roo.EventObject} e
4386          */
4387         "click" : true,
4388          /**
4389             * @event changed
4390             * Fires when the active item active state changes
4391             * @param {Roo.bootstrap.NavItem} this
4392             * @param {boolean} state the new state
4393              
4394          */
4395         'changed': true,
4396         /**
4397             * @event scrollto
4398             * Fires when scroll to element
4399             * @param {Roo.bootstrap.NavItem} this
4400             * @param {Object} options
4401             * @param {Roo.EventObject} e
4402              
4403          */
4404         'scrollto': true
4405     });
4406    
4407 };
4408
4409 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4410     
4411     href: false,
4412     html: '',
4413     badge: '',
4414     icon: false,
4415     glyphicon: false,
4416     active: false,
4417     preventDefault : false,
4418     tabId : false,
4419     tagtype : 'a',
4420     disabled : false,
4421     animateRef : false,
4422     was_active : false,
4423     
4424     getAutoCreate : function(){
4425          
4426         var cfg = {
4427             tag: 'li',
4428             cls: 'nav-item'
4429             
4430         };
4431         
4432         if (this.active) {
4433             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4434         }
4435         if (this.disabled) {
4436             cfg.cls += ' disabled';
4437         }
4438         
4439         if (this.href || this.html || this.glyphicon || this.icon) {
4440             cfg.cn = [
4441                 {
4442                     tag: this.tagtype,
4443                     href : this.href || "#",
4444                     html: this.html || ''
4445                 }
4446             ];
4447             
4448             if (this.icon) {
4449                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4450             }
4451
4452             if(this.glyphicon) {
4453                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4454             }
4455             
4456             if (this.menu) {
4457                 
4458                 cfg.cn[0].html += " <span class='caret'></span>";
4459              
4460             }
4461             
4462             if (this.badge !== '') {
4463                  
4464                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4465             }
4466         }
4467         
4468         
4469         
4470         return cfg;
4471     },
4472     initEvents: function() 
4473     {
4474         if (typeof (this.menu) != 'undefined') {
4475             this.menu.parentType = this.xtype;
4476             this.menu.triggerEl = this.el;
4477             this.menu = this.addxtype(Roo.apply({}, this.menu));
4478         }
4479         
4480         this.el.select('a',true).on('click', this.onClick, this);
4481         
4482         if(this.tagtype == 'span'){
4483             this.el.select('span',true).on('click', this.onClick, this);
4484         }
4485        
4486         // at this point parent should be available..
4487         this.parent().register(this);
4488     },
4489     
4490     onClick : function(e)
4491     {
4492         if (e.getTarget('.dropdown-menu-item')) {
4493             // did you click on a menu itemm.... - then don't trigger onclick..
4494             return;
4495         }
4496         
4497         if(
4498                 this.preventDefault || 
4499                 this.href == '#' 
4500         ){
4501             Roo.log("NavItem - prevent Default?");
4502             e.preventDefault();
4503         }
4504         
4505         if (this.disabled) {
4506             return;
4507         }
4508         
4509         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4510         if (tg && tg.transition) {
4511             Roo.log("waiting for the transitionend");
4512             return;
4513         }
4514         
4515         
4516         
4517         //Roo.log("fire event clicked");
4518         if(this.fireEvent('click', this, e) === false){
4519             return;
4520         };
4521         
4522         if(this.tagtype == 'span'){
4523             return;
4524         }
4525         
4526         //Roo.log(this.href);
4527         var ael = this.el.select('a',true).first();
4528         //Roo.log(ael);
4529         
4530         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4531             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4532             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4533                 return; // ignore... - it's a 'hash' to another page.
4534             }
4535             Roo.log("NavItem - prevent Default?");
4536             e.preventDefault();
4537             this.scrollToElement(e);
4538         }
4539         
4540         
4541         var p =  this.parent();
4542    
4543         if (['tabs','pills'].indexOf(p.type)!==-1) {
4544             if (typeof(p.setActiveItem) !== 'undefined') {
4545                 p.setActiveItem(this);
4546             }
4547         }
4548         
4549         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4550         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4551             // remove the collapsed menu expand...
4552             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4553         }
4554     },
4555     
4556     isActive: function () {
4557         return this.active
4558     },
4559     setActive : function(state, fire, is_was_active)
4560     {
4561         if (this.active && !state && this.navId) {
4562             this.was_active = true;
4563             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4564             if (nv) {
4565                 nv.clearWasActive(this);
4566             }
4567             
4568         }
4569         this.active = state;
4570         
4571         if (!state ) {
4572             this.el.removeClass('active');
4573         } else if (!this.el.hasClass('active')) {
4574             this.el.addClass('active');
4575         }
4576         if (fire) {
4577             this.fireEvent('changed', this, state);
4578         }
4579         
4580         // show a panel if it's registered and related..
4581         
4582         if (!this.navId || !this.tabId || !state || is_was_active) {
4583             return;
4584         }
4585         
4586         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4587         if (!tg) {
4588             return;
4589         }
4590         var pan = tg.getPanelByName(this.tabId);
4591         if (!pan) {
4592             return;
4593         }
4594         // if we can not flip to new panel - go back to old nav highlight..
4595         if (false == tg.showPanel(pan)) {
4596             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4597             if (nv) {
4598                 var onav = nv.getWasActive();
4599                 if (onav) {
4600                     onav.setActive(true, false, true);
4601                 }
4602             }
4603             
4604         }
4605         
4606         
4607         
4608     },
4609      // this should not be here...
4610     setDisabled : function(state)
4611     {
4612         this.disabled = state;
4613         if (!state ) {
4614             this.el.removeClass('disabled');
4615         } else if (!this.el.hasClass('disabled')) {
4616             this.el.addClass('disabled');
4617         }
4618         
4619     },
4620     
4621     /**
4622      * Fetch the element to display the tooltip on.
4623      * @return {Roo.Element} defaults to this.el
4624      */
4625     tooltipEl : function()
4626     {
4627         return this.el.select('' + this.tagtype + '', true).first();
4628     },
4629     
4630     scrollToElement : function(e)
4631     {
4632         var c = document.body;
4633         
4634         /*
4635          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4636          */
4637         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4638             c = document.documentElement;
4639         }
4640         
4641         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4642         
4643         if(!target){
4644             return;
4645         }
4646
4647         var o = target.calcOffsetsTo(c);
4648         
4649         var options = {
4650             target : target,
4651             value : o[1]
4652         };
4653         
4654         this.fireEvent('scrollto', this, options, e);
4655         
4656         Roo.get(c).scrollTo('top', options.value, true);
4657         
4658         return;
4659     }
4660 });
4661  
4662
4663  /*
4664  * - LGPL
4665  *
4666  * sidebar item
4667  *
4668  *  li
4669  *    <span> icon </span>
4670  *    <span> text </span>
4671  *    <span>badge </span>
4672  */
4673
4674 /**
4675  * @class Roo.bootstrap.NavSidebarItem
4676  * @extends Roo.bootstrap.NavItem
4677  * Bootstrap Navbar.NavSidebarItem class
4678  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4679  * {Boolean} open is the menu open
4680  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4681  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4682  * {String} buttonSize (sm|md|lg)the extra classes for the button
4683  * {Boolean} showArrow show arrow next to the text (default true)
4684  * @constructor
4685  * Create a new Navbar Button
4686  * @param {Object} config The config object
4687  */
4688 Roo.bootstrap.NavSidebarItem = function(config){
4689     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4690     this.addEvents({
4691         // raw events
4692         /**
4693          * @event click
4694          * The raw click event for the entire grid.
4695          * @param {Roo.EventObject} e
4696          */
4697         "click" : true,
4698          /**
4699             * @event changed
4700             * Fires when the active item active state changes
4701             * @param {Roo.bootstrap.NavSidebarItem} this
4702             * @param {boolean} state the new state
4703              
4704          */
4705         'changed': true
4706     });
4707    
4708 };
4709
4710 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4711     
4712     badgeWeight : 'default',
4713     
4714     open: false,
4715     
4716     buttonView : false,
4717     
4718     buttonWeight : 'default',
4719     
4720     buttonSize : 'md',
4721     
4722     showArrow : true,
4723     
4724     getAutoCreate : function(){
4725         
4726         
4727         var a = {
4728                 tag: 'a',
4729                 href : this.href || '#',
4730                 cls: '',
4731                 html : '',
4732                 cn : []
4733         };
4734         
4735         if(this.buttonView){
4736             a = {
4737                 tag: 'button',
4738                 href : this.href || '#',
4739                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4740                 html : this.html,
4741                 cn : []
4742             };
4743         }
4744         
4745         var cfg = {
4746             tag: 'li',
4747             cls: '',
4748             cn: [ a ]
4749         };
4750         
4751         if (this.active) {
4752             cfg.cls += ' active';
4753         }
4754         
4755         if (this.disabled) {
4756             cfg.cls += ' disabled';
4757         }
4758         if (this.open) {
4759             cfg.cls += ' open x-open';
4760         }
4761         // left icon..
4762         if (this.glyphicon || this.icon) {
4763             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4764             a.cn.push({ tag : 'i', cls : c }) ;
4765         }
4766         
4767         if(!this.buttonView){
4768             var span = {
4769                 tag: 'span',
4770                 html : this.html || ''
4771             };
4772
4773             a.cn.push(span);
4774             
4775         }
4776         
4777         if (this.badge !== '') {
4778             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4779         }
4780         
4781         if (this.menu) {
4782             
4783             if(this.showArrow){
4784                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4785             }
4786             
4787             a.cls += ' dropdown-toggle treeview' ;
4788         }
4789         
4790         return cfg;
4791     },
4792     
4793     initEvents : function()
4794     { 
4795         if (typeof (this.menu) != 'undefined') {
4796             this.menu.parentType = this.xtype;
4797             this.menu.triggerEl = this.el;
4798             this.menu = this.addxtype(Roo.apply({}, this.menu));
4799         }
4800         
4801         this.el.on('click', this.onClick, this);
4802         
4803         if(this.badge !== ''){
4804             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4805         }
4806         
4807     },
4808     
4809     onClick : function(e)
4810     {
4811         if(this.disabled){
4812             e.preventDefault();
4813             return;
4814         }
4815         
4816         if(this.preventDefault){
4817             e.preventDefault();
4818         }
4819         
4820         this.fireEvent('click', this);
4821     },
4822     
4823     disable : function()
4824     {
4825         this.setDisabled(true);
4826     },
4827     
4828     enable : function()
4829     {
4830         this.setDisabled(false);
4831     },
4832     
4833     setDisabled : function(state)
4834     {
4835         if(this.disabled == state){
4836             return;
4837         }
4838         
4839         this.disabled = state;
4840         
4841         if (state) {
4842             this.el.addClass('disabled');
4843             return;
4844         }
4845         
4846         this.el.removeClass('disabled');
4847         
4848         return;
4849     },
4850     
4851     setActive : function(state)
4852     {
4853         if(this.active == state){
4854             return;
4855         }
4856         
4857         this.active = state;
4858         
4859         if (state) {
4860             this.el.addClass('active');
4861             return;
4862         }
4863         
4864         this.el.removeClass('active');
4865         
4866         return;
4867     },
4868     
4869     isActive: function () 
4870     {
4871         return this.active;
4872     },
4873     
4874     setBadge : function(str)
4875     {
4876         if(!this.badgeEl){
4877             return;
4878         }
4879         
4880         this.badgeEl.dom.innerHTML = str;
4881     }
4882     
4883    
4884      
4885  
4886 });
4887  
4888
4889  /*
4890  * - LGPL
4891  *
4892  * row
4893  * 
4894  */
4895
4896 /**
4897  * @class Roo.bootstrap.Row
4898  * @extends Roo.bootstrap.Component
4899  * Bootstrap Row class (contains columns...)
4900  * 
4901  * @constructor
4902  * Create a new Row
4903  * @param {Object} config The config object
4904  */
4905
4906 Roo.bootstrap.Row = function(config){
4907     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4908 };
4909
4910 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4911     
4912     getAutoCreate : function(){
4913        return {
4914             cls: 'row clearfix'
4915        };
4916     }
4917     
4918     
4919 });
4920
4921  
4922
4923  /*
4924  * - LGPL
4925  *
4926  * element
4927  * 
4928  */
4929
4930 /**
4931  * @class Roo.bootstrap.Element
4932  * @extends Roo.bootstrap.Component
4933  * Bootstrap Element class
4934  * @cfg {String} html contents of the element
4935  * @cfg {String} tag tag of the element
4936  * @cfg {String} cls class of the element
4937  * @cfg {Boolean} preventDefault (true|false) default false
4938  * @cfg {Boolean} clickable (true|false) default false
4939  * 
4940  * @constructor
4941  * Create a new Element
4942  * @param {Object} config The config object
4943  */
4944
4945 Roo.bootstrap.Element = function(config){
4946     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4947     
4948     this.addEvents({
4949         // raw events
4950         /**
4951          * @event click
4952          * When a element is chick
4953          * @param {Roo.bootstrap.Element} this
4954          * @param {Roo.EventObject} e
4955          */
4956         "click" : true
4957     });
4958 };
4959
4960 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4961     
4962     tag: 'div',
4963     cls: '',
4964     html: '',
4965     preventDefault: false, 
4966     clickable: false,
4967     
4968     getAutoCreate : function(){
4969         
4970         var cfg = {
4971             tag: this.tag,
4972             // cls: this.cls, double assign in parent class Component.js :: onRender
4973             html: this.html
4974         };
4975         
4976         return cfg;
4977     },
4978     
4979     initEvents: function() 
4980     {
4981         Roo.bootstrap.Element.superclass.initEvents.call(this);
4982         
4983         if(this.clickable){
4984             this.el.on('click', this.onClick, this);
4985         }
4986         
4987     },
4988     
4989     onClick : function(e)
4990     {
4991         if(this.preventDefault){
4992             e.preventDefault();
4993         }
4994         
4995         this.fireEvent('click', this, e);
4996     },
4997     
4998     getValue : function()
4999     {
5000         return this.el.dom.innerHTML;
5001     },
5002     
5003     setValue : function(value)
5004     {
5005         this.el.dom.innerHTML = value;
5006     }
5007    
5008 });
5009
5010  
5011
5012  /*
5013  * - LGPL
5014  *
5015  * pagination
5016  * 
5017  */
5018
5019 /**
5020  * @class Roo.bootstrap.Pagination
5021  * @extends Roo.bootstrap.Component
5022  * Bootstrap Pagination class
5023  * @cfg {String} size xs | sm | md | lg
5024  * @cfg {Boolean} inverse false | true
5025  * 
5026  * @constructor
5027  * Create a new Pagination
5028  * @param {Object} config The config object
5029  */
5030
5031 Roo.bootstrap.Pagination = function(config){
5032     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5033 };
5034
5035 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5036     
5037     cls: false,
5038     size: false,
5039     inverse: false,
5040     
5041     getAutoCreate : function(){
5042         var cfg = {
5043             tag: 'ul',
5044                 cls: 'pagination'
5045         };
5046         if (this.inverse) {
5047             cfg.cls += ' inverse';
5048         }
5049         if (this.html) {
5050             cfg.html=this.html;
5051         }
5052         if (this.cls) {
5053             cfg.cls += " " + this.cls;
5054         }
5055         return cfg;
5056     }
5057    
5058 });
5059
5060  
5061
5062  /*
5063  * - LGPL
5064  *
5065  * Pagination item
5066  * 
5067  */
5068
5069
5070 /**
5071  * @class Roo.bootstrap.PaginationItem
5072  * @extends Roo.bootstrap.Component
5073  * Bootstrap PaginationItem class
5074  * @cfg {String} html text
5075  * @cfg {String} href the link
5076  * @cfg {Boolean} preventDefault (true | false) default true
5077  * @cfg {Boolean} active (true | false) default false
5078  * @cfg {Boolean} disabled default false
5079  * 
5080  * 
5081  * @constructor
5082  * Create a new PaginationItem
5083  * @param {Object} config The config object
5084  */
5085
5086
5087 Roo.bootstrap.PaginationItem = function(config){
5088     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5089     this.addEvents({
5090         // raw events
5091         /**
5092          * @event click
5093          * The raw click event for the entire grid.
5094          * @param {Roo.EventObject} e
5095          */
5096         "click" : true
5097     });
5098 };
5099
5100 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5101     
5102     href : false,
5103     html : false,
5104     preventDefault: true,
5105     active : false,
5106     cls : false,
5107     disabled: false,
5108     
5109     getAutoCreate : function(){
5110         var cfg= {
5111             tag: 'li',
5112             cn: [
5113                 {
5114                     tag : 'a',
5115                     href : this.href ? this.href : '#',
5116                     html : this.html ? this.html : ''
5117                 }
5118             ]
5119         };
5120         
5121         if(this.cls){
5122             cfg.cls = this.cls;
5123         }
5124         
5125         if(this.disabled){
5126             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5127         }
5128         
5129         if(this.active){
5130             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5131         }
5132         
5133         return cfg;
5134     },
5135     
5136     initEvents: function() {
5137         
5138         this.el.on('click', this.onClick, this);
5139         
5140     },
5141     onClick : function(e)
5142     {
5143         Roo.log('PaginationItem on click ');
5144         if(this.preventDefault){
5145             e.preventDefault();
5146         }
5147         
5148         if(this.disabled){
5149             return;
5150         }
5151         
5152         this.fireEvent('click', this, e);
5153     }
5154    
5155 });
5156
5157  
5158
5159  /*
5160  * - LGPL
5161  *
5162  * slider
5163  * 
5164  */
5165
5166
5167 /**
5168  * @class Roo.bootstrap.Slider
5169  * @extends Roo.bootstrap.Component
5170  * Bootstrap Slider class
5171  *    
5172  * @constructor
5173  * Create a new Slider
5174  * @param {Object} config The config object
5175  */
5176
5177 Roo.bootstrap.Slider = function(config){
5178     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5179 };
5180
5181 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5182     
5183     getAutoCreate : function(){
5184         
5185         var cfg = {
5186             tag: 'div',
5187             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5188             cn: [
5189                 {
5190                     tag: 'a',
5191                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5192                 }
5193             ]
5194         };
5195         
5196         return cfg;
5197     }
5198    
5199 });
5200
5201  /*
5202  * Based on:
5203  * Ext JS Library 1.1.1
5204  * Copyright(c) 2006-2007, Ext JS, LLC.
5205  *
5206  * Originally Released Under LGPL - original licence link has changed is not relivant.
5207  *
5208  * Fork - LGPL
5209  * <script type="text/javascript">
5210  */
5211  
5212
5213 /**
5214  * @class Roo.grid.ColumnModel
5215  * @extends Roo.util.Observable
5216  * This is the default implementation of a ColumnModel used by the Grid. It defines
5217  * the columns in the grid.
5218  * <br>Usage:<br>
5219  <pre><code>
5220  var colModel = new Roo.grid.ColumnModel([
5221         {header: "Ticker", width: 60, sortable: true, locked: true},
5222         {header: "Company Name", width: 150, sortable: true},
5223         {header: "Market Cap.", width: 100, sortable: true},
5224         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5225         {header: "Employees", width: 100, sortable: true, resizable: false}
5226  ]);
5227  </code></pre>
5228  * <p>
5229  
5230  * The config options listed for this class are options which may appear in each
5231  * individual column definition.
5232  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5233  * @constructor
5234  * @param {Object} config An Array of column config objects. See this class's
5235  * config objects for details.
5236 */
5237 Roo.grid.ColumnModel = function(config){
5238         /**
5239      * The config passed into the constructor
5240      */
5241     this.config = config;
5242     this.lookup = {};
5243
5244     // if no id, create one
5245     // if the column does not have a dataIndex mapping,
5246     // map it to the order it is in the config
5247     for(var i = 0, len = config.length; i < len; i++){
5248         var c = config[i];
5249         if(typeof c.dataIndex == "undefined"){
5250             c.dataIndex = i;
5251         }
5252         if(typeof c.renderer == "string"){
5253             c.renderer = Roo.util.Format[c.renderer];
5254         }
5255         if(typeof c.id == "undefined"){
5256             c.id = Roo.id();
5257         }
5258         if(c.editor && c.editor.xtype){
5259             c.editor  = Roo.factory(c.editor, Roo.grid);
5260         }
5261         if(c.editor && c.editor.isFormField){
5262             c.editor = new Roo.grid.GridEditor(c.editor);
5263         }
5264         this.lookup[c.id] = c;
5265     }
5266
5267     /**
5268      * The width of columns which have no width specified (defaults to 100)
5269      * @type Number
5270      */
5271     this.defaultWidth = 100;
5272
5273     /**
5274      * Default sortable of columns which have no sortable specified (defaults to false)
5275      * @type Boolean
5276      */
5277     this.defaultSortable = false;
5278
5279     this.addEvents({
5280         /**
5281              * @event widthchange
5282              * Fires when the width of a column changes.
5283              * @param {ColumnModel} this
5284              * @param {Number} columnIndex The column index
5285              * @param {Number} newWidth The new width
5286              */
5287             "widthchange": true,
5288         /**
5289              * @event headerchange
5290              * Fires when the text of a header changes.
5291              * @param {ColumnModel} this
5292              * @param {Number} columnIndex The column index
5293              * @param {Number} newText The new header text
5294              */
5295             "headerchange": true,
5296         /**
5297              * @event hiddenchange
5298              * Fires when a column is hidden or "unhidden".
5299              * @param {ColumnModel} this
5300              * @param {Number} columnIndex The column index
5301              * @param {Boolean} hidden true if hidden, false otherwise
5302              */
5303             "hiddenchange": true,
5304             /**
5305          * @event columnmoved
5306          * Fires when a column is moved.
5307          * @param {ColumnModel} this
5308          * @param {Number} oldIndex
5309          * @param {Number} newIndex
5310          */
5311         "columnmoved" : true,
5312         /**
5313          * @event columlockchange
5314          * Fires when a column's locked state is changed
5315          * @param {ColumnModel} this
5316          * @param {Number} colIndex
5317          * @param {Boolean} locked true if locked
5318          */
5319         "columnlockchange" : true
5320     });
5321     Roo.grid.ColumnModel.superclass.constructor.call(this);
5322 };
5323 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5324     /**
5325      * @cfg {String} header The header text to display in the Grid view.
5326      */
5327     /**
5328      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5329      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5330      * specified, the column's index is used as an index into the Record's data Array.
5331      */
5332     /**
5333      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5334      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5335      */
5336     /**
5337      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5338      * Defaults to the value of the {@link #defaultSortable} property.
5339      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5340      */
5341     /**
5342      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5343      */
5344     /**
5345      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5346      */
5347     /**
5348      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5349      */
5350     /**
5351      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5352      */
5353     /**
5354      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5355      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5356      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5357      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5358      */
5359        /**
5360      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5361      */
5362     /**
5363      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5364      */
5365     /**
5366      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5367      */
5368     /**
5369      * @cfg {String} cursor (Optional)
5370      */
5371     /**
5372      * @cfg {String} tooltip (Optional)
5373      */
5374     /**
5375      * @cfg {Number} xs (Optional)
5376      */
5377     /**
5378      * @cfg {Number} sm (Optional)
5379      */
5380     /**
5381      * @cfg {Number} md (Optional)
5382      */
5383     /**
5384      * @cfg {Number} lg (Optional)
5385      */
5386     /**
5387      * Returns the id of the column at the specified index.
5388      * @param {Number} index The column index
5389      * @return {String} the id
5390      */
5391     getColumnId : function(index){
5392         return this.config[index].id;
5393     },
5394
5395     /**
5396      * Returns the column for a specified id.
5397      * @param {String} id The column id
5398      * @return {Object} the column
5399      */
5400     getColumnById : function(id){
5401         return this.lookup[id];
5402     },
5403
5404     
5405     /**
5406      * Returns the column for a specified dataIndex.
5407      * @param {String} dataIndex The column dataIndex
5408      * @return {Object|Boolean} the column or false if not found
5409      */
5410     getColumnByDataIndex: function(dataIndex){
5411         var index = this.findColumnIndex(dataIndex);
5412         return index > -1 ? this.config[index] : false;
5413     },
5414     
5415     /**
5416      * Returns the index for a specified column id.
5417      * @param {String} id The column id
5418      * @return {Number} the index, or -1 if not found
5419      */
5420     getIndexById : function(id){
5421         for(var i = 0, len = this.config.length; i < len; i++){
5422             if(this.config[i].id == id){
5423                 return i;
5424             }
5425         }
5426         return -1;
5427     },
5428     
5429     /**
5430      * Returns the index for a specified column dataIndex.
5431      * @param {String} dataIndex The column dataIndex
5432      * @return {Number} the index, or -1 if not found
5433      */
5434     
5435     findColumnIndex : function(dataIndex){
5436         for(var i = 0, len = this.config.length; i < len; i++){
5437             if(this.config[i].dataIndex == dataIndex){
5438                 return i;
5439             }
5440         }
5441         return -1;
5442     },
5443     
5444     
5445     moveColumn : function(oldIndex, newIndex){
5446         var c = this.config[oldIndex];
5447         this.config.splice(oldIndex, 1);
5448         this.config.splice(newIndex, 0, c);
5449         this.dataMap = null;
5450         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5451     },
5452
5453     isLocked : function(colIndex){
5454         return this.config[colIndex].locked === true;
5455     },
5456
5457     setLocked : function(colIndex, value, suppressEvent){
5458         if(this.isLocked(colIndex) == value){
5459             return;
5460         }
5461         this.config[colIndex].locked = value;
5462         if(!suppressEvent){
5463             this.fireEvent("columnlockchange", this, colIndex, value);
5464         }
5465     },
5466
5467     getTotalLockedWidth : function(){
5468         var totalWidth = 0;
5469         for(var i = 0; i < this.config.length; i++){
5470             if(this.isLocked(i) && !this.isHidden(i)){
5471                 this.totalWidth += this.getColumnWidth(i);
5472             }
5473         }
5474         return totalWidth;
5475     },
5476
5477     getLockedCount : function(){
5478         for(var i = 0, len = this.config.length; i < len; i++){
5479             if(!this.isLocked(i)){
5480                 return i;
5481             }
5482         }
5483         
5484         return this.config.length;
5485     },
5486
5487     /**
5488      * Returns the number of columns.
5489      * @return {Number}
5490      */
5491     getColumnCount : function(visibleOnly){
5492         if(visibleOnly === true){
5493             var c = 0;
5494             for(var i = 0, len = this.config.length; i < len; i++){
5495                 if(!this.isHidden(i)){
5496                     c++;
5497                 }
5498             }
5499             return c;
5500         }
5501         return this.config.length;
5502     },
5503
5504     /**
5505      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5506      * @param {Function} fn
5507      * @param {Object} scope (optional)
5508      * @return {Array} result
5509      */
5510     getColumnsBy : function(fn, scope){
5511         var r = [];
5512         for(var i = 0, len = this.config.length; i < len; i++){
5513             var c = this.config[i];
5514             if(fn.call(scope||this, c, i) === true){
5515                 r[r.length] = c;
5516             }
5517         }
5518         return r;
5519     },
5520
5521     /**
5522      * Returns true if the specified column is sortable.
5523      * @param {Number} col The column index
5524      * @return {Boolean}
5525      */
5526     isSortable : function(col){
5527         if(typeof this.config[col].sortable == "undefined"){
5528             return this.defaultSortable;
5529         }
5530         return this.config[col].sortable;
5531     },
5532
5533     /**
5534      * Returns the rendering (formatting) function defined for the column.
5535      * @param {Number} col The column index.
5536      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5537      */
5538     getRenderer : function(col){
5539         if(!this.config[col].renderer){
5540             return Roo.grid.ColumnModel.defaultRenderer;
5541         }
5542         return this.config[col].renderer;
5543     },
5544
5545     /**
5546      * Sets the rendering (formatting) function for a column.
5547      * @param {Number} col The column index
5548      * @param {Function} fn The function to use to process the cell's raw data
5549      * to return HTML markup for the grid view. The render function is called with
5550      * the following parameters:<ul>
5551      * <li>Data value.</li>
5552      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5553      * <li>css A CSS style string to apply to the table cell.</li>
5554      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5555      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5556      * <li>Row index</li>
5557      * <li>Column index</li>
5558      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5559      */
5560     setRenderer : function(col, fn){
5561         this.config[col].renderer = fn;
5562     },
5563
5564     /**
5565      * Returns the width for the specified column.
5566      * @param {Number} col The column index
5567      * @return {Number}
5568      */
5569     getColumnWidth : function(col){
5570         return this.config[col].width * 1 || this.defaultWidth;
5571     },
5572
5573     /**
5574      * Sets the width for a column.
5575      * @param {Number} col The column index
5576      * @param {Number} width The new width
5577      */
5578     setColumnWidth : function(col, width, suppressEvent){
5579         this.config[col].width = width;
5580         this.totalWidth = null;
5581         if(!suppressEvent){
5582              this.fireEvent("widthchange", this, col, width);
5583         }
5584     },
5585
5586     /**
5587      * Returns the total width of all columns.
5588      * @param {Boolean} includeHidden True to include hidden column widths
5589      * @return {Number}
5590      */
5591     getTotalWidth : function(includeHidden){
5592         if(!this.totalWidth){
5593             this.totalWidth = 0;
5594             for(var i = 0, len = this.config.length; i < len; i++){
5595                 if(includeHidden || !this.isHidden(i)){
5596                     this.totalWidth += this.getColumnWidth(i);
5597                 }
5598             }
5599         }
5600         return this.totalWidth;
5601     },
5602
5603     /**
5604      * Returns the header for the specified column.
5605      * @param {Number} col The column index
5606      * @return {String}
5607      */
5608     getColumnHeader : function(col){
5609         return this.config[col].header;
5610     },
5611
5612     /**
5613      * Sets the header for a column.
5614      * @param {Number} col The column index
5615      * @param {String} header The new header
5616      */
5617     setColumnHeader : function(col, header){
5618         this.config[col].header = header;
5619         this.fireEvent("headerchange", this, col, header);
5620     },
5621
5622     /**
5623      * Returns the tooltip for the specified column.
5624      * @param {Number} col The column index
5625      * @return {String}
5626      */
5627     getColumnTooltip : function(col){
5628             return this.config[col].tooltip;
5629     },
5630     /**
5631      * Sets the tooltip for a column.
5632      * @param {Number} col The column index
5633      * @param {String} tooltip The new tooltip
5634      */
5635     setColumnTooltip : function(col, tooltip){
5636             this.config[col].tooltip = tooltip;
5637     },
5638
5639     /**
5640      * Returns the dataIndex for the specified column.
5641      * @param {Number} col The column index
5642      * @return {Number}
5643      */
5644     getDataIndex : function(col){
5645         return this.config[col].dataIndex;
5646     },
5647
5648     /**
5649      * Sets the dataIndex for a column.
5650      * @param {Number} col The column index
5651      * @param {Number} dataIndex The new dataIndex
5652      */
5653     setDataIndex : function(col, dataIndex){
5654         this.config[col].dataIndex = dataIndex;
5655     },
5656
5657     
5658     
5659     /**
5660      * Returns true if the cell is editable.
5661      * @param {Number} colIndex The column index
5662      * @param {Number} rowIndex The row index - this is nto actually used..?
5663      * @return {Boolean}
5664      */
5665     isCellEditable : function(colIndex, rowIndex){
5666         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5667     },
5668
5669     /**
5670      * Returns the editor defined for the cell/column.
5671      * return false or null to disable editing.
5672      * @param {Number} colIndex The column index
5673      * @param {Number} rowIndex The row index
5674      * @return {Object}
5675      */
5676     getCellEditor : function(colIndex, rowIndex){
5677         return this.config[colIndex].editor;
5678     },
5679
5680     /**
5681      * Sets if a column is editable.
5682      * @param {Number} col The column index
5683      * @param {Boolean} editable True if the column is editable
5684      */
5685     setEditable : function(col, editable){
5686         this.config[col].editable = editable;
5687     },
5688
5689
5690     /**
5691      * Returns true if the column is hidden.
5692      * @param {Number} colIndex The column index
5693      * @return {Boolean}
5694      */
5695     isHidden : function(colIndex){
5696         return this.config[colIndex].hidden;
5697     },
5698
5699
5700     /**
5701      * Returns true if the column width cannot be changed
5702      */
5703     isFixed : function(colIndex){
5704         return this.config[colIndex].fixed;
5705     },
5706
5707     /**
5708      * Returns true if the column can be resized
5709      * @return {Boolean}
5710      */
5711     isResizable : function(colIndex){
5712         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5713     },
5714     /**
5715      * Sets if a column is hidden.
5716      * @param {Number} colIndex The column index
5717      * @param {Boolean} hidden True if the column is hidden
5718      */
5719     setHidden : function(colIndex, hidden){
5720         this.config[colIndex].hidden = hidden;
5721         this.totalWidth = null;
5722         this.fireEvent("hiddenchange", this, colIndex, hidden);
5723     },
5724
5725     /**
5726      * Sets the editor for a column.
5727      * @param {Number} col The column index
5728      * @param {Object} editor The editor object
5729      */
5730     setEditor : function(col, editor){
5731         this.config[col].editor = editor;
5732     }
5733 });
5734
5735 Roo.grid.ColumnModel.defaultRenderer = function(value)
5736 {
5737     if(typeof value == "object") {
5738         return value;
5739     }
5740         if(typeof value == "string" && value.length < 1){
5741             return "&#160;";
5742         }
5743     
5744         return String.format("{0}", value);
5745 };
5746
5747 // Alias for backwards compatibility
5748 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5749 /*
5750  * Based on:
5751  * Ext JS Library 1.1.1
5752  * Copyright(c) 2006-2007, Ext JS, LLC.
5753  *
5754  * Originally Released Under LGPL - original licence link has changed is not relivant.
5755  *
5756  * Fork - LGPL
5757  * <script type="text/javascript">
5758  */
5759  
5760 /**
5761  * @class Roo.LoadMask
5762  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5763  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5764  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5765  * element's UpdateManager load indicator and will be destroyed after the initial load.
5766  * @constructor
5767  * Create a new LoadMask
5768  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5769  * @param {Object} config The config object
5770  */
5771 Roo.LoadMask = function(el, config){
5772     this.el = Roo.get(el);
5773     Roo.apply(this, config);
5774     if(this.store){
5775         this.store.on('beforeload', this.onBeforeLoad, this);
5776         this.store.on('load', this.onLoad, this);
5777         this.store.on('loadexception', this.onLoadException, this);
5778         this.removeMask = false;
5779     }else{
5780         var um = this.el.getUpdateManager();
5781         um.showLoadIndicator = false; // disable the default indicator
5782         um.on('beforeupdate', this.onBeforeLoad, this);
5783         um.on('update', this.onLoad, this);
5784         um.on('failure', this.onLoad, this);
5785         this.removeMask = true;
5786     }
5787 };
5788
5789 Roo.LoadMask.prototype = {
5790     /**
5791      * @cfg {Boolean} removeMask
5792      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5793      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5794      */
5795     /**
5796      * @cfg {String} msg
5797      * The text to display in a centered loading message box (defaults to 'Loading...')
5798      */
5799     msg : 'Loading...',
5800     /**
5801      * @cfg {String} msgCls
5802      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5803      */
5804     msgCls : 'x-mask-loading',
5805
5806     /**
5807      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5808      * @type Boolean
5809      */
5810     disabled: false,
5811
5812     /**
5813      * Disables the mask to prevent it from being displayed
5814      */
5815     disable : function(){
5816        this.disabled = true;
5817     },
5818
5819     /**
5820      * Enables the mask so that it can be displayed
5821      */
5822     enable : function(){
5823         this.disabled = false;
5824     },
5825     
5826     onLoadException : function()
5827     {
5828         Roo.log(arguments);
5829         
5830         if (typeof(arguments[3]) != 'undefined') {
5831             Roo.MessageBox.alert("Error loading",arguments[3]);
5832         } 
5833         /*
5834         try {
5835             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5836                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5837             }   
5838         } catch(e) {
5839             
5840         }
5841         */
5842     
5843         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5844     },
5845     // private
5846     onLoad : function()
5847     {
5848         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5849     },
5850
5851     // private
5852     onBeforeLoad : function(){
5853         if(!this.disabled){
5854             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5855         }
5856     },
5857
5858     // private
5859     destroy : function(){
5860         if(this.store){
5861             this.store.un('beforeload', this.onBeforeLoad, this);
5862             this.store.un('load', this.onLoad, this);
5863             this.store.un('loadexception', this.onLoadException, this);
5864         }else{
5865             var um = this.el.getUpdateManager();
5866             um.un('beforeupdate', this.onBeforeLoad, this);
5867             um.un('update', this.onLoad, this);
5868             um.un('failure', this.onLoad, this);
5869         }
5870     }
5871 };/*
5872  * - LGPL
5873  *
5874  * table
5875  * 
5876  */
5877
5878 /**
5879  * @class Roo.bootstrap.Table
5880  * @extends Roo.bootstrap.Component
5881  * Bootstrap Table class
5882  * @cfg {String} cls table class
5883  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5884  * @cfg {String} bgcolor Specifies the background color for a table
5885  * @cfg {Number} border Specifies whether the table cells should have borders or not
5886  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5887  * @cfg {Number} cellspacing Specifies the space between cells
5888  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5889  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5890  * @cfg {String} sortable Specifies that the table should be sortable
5891  * @cfg {String} summary Specifies a summary of the content of a table
5892  * @cfg {Number} width Specifies the width of a table
5893  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5894  * 
5895  * @cfg {boolean} striped Should the rows be alternative striped
5896  * @cfg {boolean} bordered Add borders to the table
5897  * @cfg {boolean} hover Add hover highlighting
5898  * @cfg {boolean} condensed Format condensed
5899  * @cfg {boolean} responsive Format condensed
5900  * @cfg {Boolean} loadMask (true|false) default false
5901  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5902  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5903  * @cfg {Boolean} rowSelection (true|false) default false
5904  * @cfg {Boolean} cellSelection (true|false) default false
5905  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5906  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5907  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5908  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5909  
5910  * 
5911  * @constructor
5912  * Create a new Table
5913  * @param {Object} config The config object
5914  */
5915
5916 Roo.bootstrap.Table = function(config){
5917     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5918     
5919   
5920     
5921     // BC...
5922     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5923     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5924     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5925     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5926     
5927     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5928     if (this.sm) {
5929         this.sm.grid = this;
5930         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5931         this.sm = this.selModel;
5932         this.sm.xmodule = this.xmodule || false;
5933     }
5934     
5935     if (this.cm && typeof(this.cm.config) == 'undefined') {
5936         this.colModel = new Roo.grid.ColumnModel(this.cm);
5937         this.cm = this.colModel;
5938         this.cm.xmodule = this.xmodule || false;
5939     }
5940     if (this.store) {
5941         this.store= Roo.factory(this.store, Roo.data);
5942         this.ds = this.store;
5943         this.ds.xmodule = this.xmodule || false;
5944          
5945     }
5946     if (this.footer && this.store) {
5947         this.footer.dataSource = this.ds;
5948         this.footer = Roo.factory(this.footer);
5949     }
5950     
5951     /** @private */
5952     this.addEvents({
5953         /**
5954          * @event cellclick
5955          * Fires when a cell is clicked
5956          * @param {Roo.bootstrap.Table} this
5957          * @param {Roo.Element} el
5958          * @param {Number} rowIndex
5959          * @param {Number} columnIndex
5960          * @param {Roo.EventObject} e
5961          */
5962         "cellclick" : true,
5963         /**
5964          * @event celldblclick
5965          * Fires when a cell is double clicked
5966          * @param {Roo.bootstrap.Table} this
5967          * @param {Roo.Element} el
5968          * @param {Number} rowIndex
5969          * @param {Number} columnIndex
5970          * @param {Roo.EventObject} e
5971          */
5972         "celldblclick" : true,
5973         /**
5974          * @event rowclick
5975          * Fires when a row is clicked
5976          * @param {Roo.bootstrap.Table} this
5977          * @param {Roo.Element} el
5978          * @param {Number} rowIndex
5979          * @param {Roo.EventObject} e
5980          */
5981         "rowclick" : true,
5982         /**
5983          * @event rowdblclick
5984          * Fires when a row is double clicked
5985          * @param {Roo.bootstrap.Table} this
5986          * @param {Roo.Element} el
5987          * @param {Number} rowIndex
5988          * @param {Roo.EventObject} e
5989          */
5990         "rowdblclick" : true,
5991         /**
5992          * @event mouseover
5993          * Fires when a mouseover occur
5994          * @param {Roo.bootstrap.Table} this
5995          * @param {Roo.Element} el
5996          * @param {Number} rowIndex
5997          * @param {Number} columnIndex
5998          * @param {Roo.EventObject} e
5999          */
6000         "mouseover" : true,
6001         /**
6002          * @event mouseout
6003          * Fires when a mouseout occur
6004          * @param {Roo.bootstrap.Table} this
6005          * @param {Roo.Element} el
6006          * @param {Number} rowIndex
6007          * @param {Number} columnIndex
6008          * @param {Roo.EventObject} e
6009          */
6010         "mouseout" : true,
6011         /**
6012          * @event rowclass
6013          * Fires when a row is rendered, so you can change add a style to it.
6014          * @param {Roo.bootstrap.Table} this
6015          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6016          */
6017         'rowclass' : true,
6018           /**
6019          * @event rowsrendered
6020          * Fires when all the  rows have been rendered
6021          * @param {Roo.bootstrap.Table} this
6022          */
6023         'rowsrendered' : true,
6024         /**
6025          * @event contextmenu
6026          * The raw contextmenu event for the entire grid.
6027          * @param {Roo.EventObject} e
6028          */
6029         "contextmenu" : true,
6030         /**
6031          * @event rowcontextmenu
6032          * Fires when a row is right clicked
6033          * @param {Roo.bootstrap.Table} this
6034          * @param {Number} rowIndex
6035          * @param {Roo.EventObject} e
6036          */
6037         "rowcontextmenu" : true,
6038         /**
6039          * @event cellcontextmenu
6040          * Fires when a cell is right clicked
6041          * @param {Roo.bootstrap.Table} this
6042          * @param {Number} rowIndex
6043          * @param {Number} cellIndex
6044          * @param {Roo.EventObject} e
6045          */
6046          "cellcontextmenu" : true,
6047          /**
6048          * @event headercontextmenu
6049          * Fires when a header is right clicked
6050          * @param {Roo.bootstrap.Table} this
6051          * @param {Number} columnIndex
6052          * @param {Roo.EventObject} e
6053          */
6054         "headercontextmenu" : true
6055     });
6056 };
6057
6058 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6059     
6060     cls: false,
6061     align: false,
6062     bgcolor: false,
6063     border: false,
6064     cellpadding: false,
6065     cellspacing: false,
6066     frame: false,
6067     rules: false,
6068     sortable: false,
6069     summary: false,
6070     width: false,
6071     striped : false,
6072     scrollBody : false,
6073     bordered: false,
6074     hover:  false,
6075     condensed : false,
6076     responsive : false,
6077     sm : false,
6078     cm : false,
6079     store : false,
6080     loadMask : false,
6081     footerShow : true,
6082     headerShow : true,
6083   
6084     rowSelection : false,
6085     cellSelection : false,
6086     layout : false,
6087     
6088     // Roo.Element - the tbody
6089     mainBody: false,
6090     // Roo.Element - thead element
6091     mainHead: false,
6092     
6093     container: false, // used by gridpanel...
6094     
6095     lazyLoad : false,
6096     
6097     CSS : Roo.util.CSS,
6098     
6099     auto_hide_footer : false,
6100     
6101     getAutoCreate : function()
6102     {
6103         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6104         
6105         cfg = {
6106             tag: 'table',
6107             cls : 'table',
6108             cn : []
6109         };
6110         if (this.scrollBody) {
6111             cfg.cls += ' table-body-fixed';
6112         }    
6113         if (this.striped) {
6114             cfg.cls += ' table-striped';
6115         }
6116         
6117         if (this.hover) {
6118             cfg.cls += ' table-hover';
6119         }
6120         if (this.bordered) {
6121             cfg.cls += ' table-bordered';
6122         }
6123         if (this.condensed) {
6124             cfg.cls += ' table-condensed';
6125         }
6126         if (this.responsive) {
6127             cfg.cls += ' table-responsive';
6128         }
6129         
6130         if (this.cls) {
6131             cfg.cls+=  ' ' +this.cls;
6132         }
6133         
6134         // this lot should be simplifed...
6135         var _t = this;
6136         var cp = [
6137             'align',
6138             'bgcolor',
6139             'border',
6140             'cellpadding',
6141             'cellspacing',
6142             'frame',
6143             'rules',
6144             'sortable',
6145             'summary',
6146             'width'
6147         ].forEach(function(k) {
6148             if (_t[k]) {
6149                 cfg[k] = _t[k];
6150             }
6151         });
6152         
6153         
6154         if (this.layout) {
6155             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6156         }
6157         
6158         if(this.store || this.cm){
6159             if(this.headerShow){
6160                 cfg.cn.push(this.renderHeader());
6161             }
6162             
6163             cfg.cn.push(this.renderBody());
6164             
6165             if(this.footerShow){
6166                 cfg.cn.push(this.renderFooter());
6167             }
6168             // where does this come from?
6169             //cfg.cls+=  ' TableGrid';
6170         }
6171         
6172         return { cn : [ cfg ] };
6173     },
6174     
6175     initEvents : function()
6176     {   
6177         if(!this.store || !this.cm){
6178             return;
6179         }
6180         if (this.selModel) {
6181             this.selModel.initEvents();
6182         }
6183         
6184         
6185         //Roo.log('initEvents with ds!!!!');
6186         
6187         this.mainBody = this.el.select('tbody', true).first();
6188         this.mainHead = this.el.select('thead', true).first();
6189         this.mainFoot = this.el.select('tfoot', true).first();
6190         
6191         
6192         
6193         var _this = this;
6194         
6195         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6196             e.on('click', _this.sort, _this);
6197         });
6198         
6199         this.mainBody.on("click", this.onClick, this);
6200         this.mainBody.on("dblclick", this.onDblClick, this);
6201         
6202         // why is this done????? = it breaks dialogs??
6203         //this.parent().el.setStyle('position', 'relative');
6204         
6205         
6206         if (this.footer) {
6207             this.footer.parentId = this.id;
6208             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6209             
6210             if(this.lazyLoad){
6211                 this.el.select('tfoot tr td').first().addClass('hide');
6212             }
6213         } 
6214         
6215         if(this.loadMask) {
6216             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6217         }
6218         
6219         this.store.on('load', this.onLoad, this);
6220         this.store.on('beforeload', this.onBeforeLoad, this);
6221         this.store.on('update', this.onUpdate, this);
6222         this.store.on('add', this.onAdd, this);
6223         this.store.on("clear", this.clear, this);
6224         
6225         this.el.on("contextmenu", this.onContextMenu, this);
6226         
6227         this.mainBody.on('scroll', this.onBodyScroll, this);
6228         
6229         this.cm.on("headerchange", this.onHeaderChange, this);
6230         
6231         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6232         
6233     },
6234     
6235     onContextMenu : function(e, t)
6236     {
6237         this.processEvent("contextmenu", e);
6238     },
6239     
6240     processEvent : function(name, e)
6241     {
6242         if (name != 'touchstart' ) {
6243             this.fireEvent(name, e);    
6244         }
6245         
6246         var t = e.getTarget();
6247         
6248         var cell = Roo.get(t);
6249         
6250         if(!cell){
6251             return;
6252         }
6253         
6254         if(cell.findParent('tfoot', false, true)){
6255             return;
6256         }
6257         
6258         if(cell.findParent('thead', false, true)){
6259             
6260             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6261                 cell = Roo.get(t).findParent('th', false, true);
6262                 if (!cell) {
6263                     Roo.log("failed to find th in thead?");
6264                     Roo.log(e.getTarget());
6265                     return;
6266                 }
6267             }
6268             
6269             var cellIndex = cell.dom.cellIndex;
6270             
6271             var ename = name == 'touchstart' ? 'click' : name;
6272             this.fireEvent("header" + ename, this, cellIndex, e);
6273             
6274             return;
6275         }
6276         
6277         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6278             cell = Roo.get(t).findParent('td', false, true);
6279             if (!cell) {
6280                 Roo.log("failed to find th in tbody?");
6281                 Roo.log(e.getTarget());
6282                 return;
6283             }
6284         }
6285         
6286         var row = cell.findParent('tr', false, true);
6287         var cellIndex = cell.dom.cellIndex;
6288         var rowIndex = row.dom.rowIndex - 1;
6289         
6290         if(row !== false){
6291             
6292             this.fireEvent("row" + name, this, rowIndex, e);
6293             
6294             if(cell !== false){
6295             
6296                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6297             }
6298         }
6299         
6300     },
6301     
6302     onMouseover : function(e, el)
6303     {
6304         var cell = Roo.get(el);
6305         
6306         if(!cell){
6307             return;
6308         }
6309         
6310         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6311             cell = cell.findParent('td', false, true);
6312         }
6313         
6314         var row = cell.findParent('tr', false, true);
6315         var cellIndex = cell.dom.cellIndex;
6316         var rowIndex = row.dom.rowIndex - 1; // start from 0
6317         
6318         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6319         
6320     },
6321     
6322     onMouseout : function(e, el)
6323     {
6324         var cell = Roo.get(el);
6325         
6326         if(!cell){
6327             return;
6328         }
6329         
6330         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6331             cell = cell.findParent('td', false, true);
6332         }
6333         
6334         var row = cell.findParent('tr', false, true);
6335         var cellIndex = cell.dom.cellIndex;
6336         var rowIndex = row.dom.rowIndex - 1; // start from 0
6337         
6338         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6339         
6340     },
6341     
6342     onClick : function(e, el)
6343     {
6344         var cell = Roo.get(el);
6345         
6346         if(!cell || (!this.cellSelection && !this.rowSelection)){
6347             return;
6348         }
6349         
6350         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6351             cell = cell.findParent('td', false, true);
6352         }
6353         
6354         if(!cell || typeof(cell) == 'undefined'){
6355             return;
6356         }
6357         
6358         var row = cell.findParent('tr', false, true);
6359         
6360         if(!row || typeof(row) == 'undefined'){
6361             return;
6362         }
6363         
6364         var cellIndex = cell.dom.cellIndex;
6365         var rowIndex = this.getRowIndex(row);
6366         
6367         // why??? - should these not be based on SelectionModel?
6368         if(this.cellSelection){
6369             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6370         }
6371         
6372         if(this.rowSelection){
6373             this.fireEvent('rowclick', this, row, rowIndex, e);
6374         }
6375         
6376         
6377     },
6378         
6379     onDblClick : function(e,el)
6380     {
6381         var cell = Roo.get(el);
6382         
6383         if(!cell || (!this.cellSelection && !this.rowSelection)){
6384             return;
6385         }
6386         
6387         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6388             cell = cell.findParent('td', false, true);
6389         }
6390         
6391         if(!cell || typeof(cell) == 'undefined'){
6392             return;
6393         }
6394         
6395         var row = cell.findParent('tr', false, true);
6396         
6397         if(!row || typeof(row) == 'undefined'){
6398             return;
6399         }
6400         
6401         var cellIndex = cell.dom.cellIndex;
6402         var rowIndex = this.getRowIndex(row);
6403         
6404         if(this.cellSelection){
6405             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6406         }
6407         
6408         if(this.rowSelection){
6409             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6410         }
6411     },
6412     
6413     sort : function(e,el)
6414     {
6415         var col = Roo.get(el);
6416         
6417         if(!col.hasClass('sortable')){
6418             return;
6419         }
6420         
6421         var sort = col.attr('sort');
6422         var dir = 'ASC';
6423         
6424         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6425             dir = 'DESC';
6426         }
6427         
6428         this.store.sortInfo = {field : sort, direction : dir};
6429         
6430         if (this.footer) {
6431             Roo.log("calling footer first");
6432             this.footer.onClick('first');
6433         } else {
6434         
6435             this.store.load({ params : { start : 0 } });
6436         }
6437     },
6438     
6439     renderHeader : function()
6440     {
6441         var header = {
6442             tag: 'thead',
6443             cn : []
6444         };
6445         
6446         var cm = this.cm;
6447         this.totalWidth = 0;
6448         
6449         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6450             
6451             var config = cm.config[i];
6452             
6453             var c = {
6454                 tag: 'th',
6455                 cls : 'x-hcol-' + i,
6456                 style : '',
6457                 html: cm.getColumnHeader(i)
6458             };
6459             
6460             var hh = '';
6461             
6462             if(typeof(config.sortable) != 'undefined' && config.sortable){
6463                 c.cls = 'sortable';
6464                 c.html = '<i class="glyphicon"></i>' + c.html;
6465             }
6466             
6467             if(typeof(config.lgHeader) != 'undefined'){
6468                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6469             }
6470             
6471             if(typeof(config.mdHeader) != 'undefined'){
6472                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6473             }
6474             
6475             if(typeof(config.smHeader) != 'undefined'){
6476                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6477             }
6478             
6479             if(typeof(config.xsHeader) != 'undefined'){
6480                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6481             }
6482             
6483             if(hh.length){
6484                 c.html = hh;
6485             }
6486             
6487             if(typeof(config.tooltip) != 'undefined'){
6488                 c.tooltip = config.tooltip;
6489             }
6490             
6491             if(typeof(config.colspan) != 'undefined'){
6492                 c.colspan = config.colspan;
6493             }
6494             
6495             if(typeof(config.hidden) != 'undefined' && config.hidden){
6496                 c.style += ' display:none;';
6497             }
6498             
6499             if(typeof(config.dataIndex) != 'undefined'){
6500                 c.sort = config.dataIndex;
6501             }
6502             
6503            
6504             
6505             if(typeof(config.align) != 'undefined' && config.align.length){
6506                 c.style += ' text-align:' + config.align + ';';
6507             }
6508             
6509             if(typeof(config.width) != 'undefined'){
6510                 c.style += ' width:' + config.width + 'px;';
6511                 this.totalWidth += config.width;
6512             } else {
6513                 this.totalWidth += 100; // assume minimum of 100 per column?
6514             }
6515             
6516             if(typeof(config.cls) != 'undefined'){
6517                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6518             }
6519             
6520             ['xs','sm','md','lg'].map(function(size){
6521                 
6522                 if(typeof(config[size]) == 'undefined'){
6523                     return;
6524                 }
6525                 
6526                 if (!config[size]) { // 0 = hidden
6527                     c.cls += ' hidden-' + size;
6528                     return;
6529                 }
6530                 
6531                 c.cls += ' col-' + size + '-' + config[size];
6532
6533             });
6534             
6535             header.cn.push(c)
6536         }
6537         
6538         return header;
6539     },
6540     
6541     renderBody : function()
6542     {
6543         var body = {
6544             tag: 'tbody',
6545             cn : [
6546                 {
6547                     tag: 'tr',
6548                     cn : [
6549                         {
6550                             tag : 'td',
6551                             colspan :  this.cm.getColumnCount()
6552                         }
6553                     ]
6554                 }
6555             ]
6556         };
6557         
6558         return body;
6559     },
6560     
6561     renderFooter : function()
6562     {
6563         var footer = {
6564             tag: 'tfoot',
6565             cn : [
6566                 {
6567                     tag: 'tr',
6568                     cn : [
6569                         {
6570                             tag : 'td',
6571                             colspan :  this.cm.getColumnCount()
6572                         }
6573                     ]
6574                 }
6575             ]
6576         };
6577         
6578         return footer;
6579     },
6580     
6581     
6582     
6583     onLoad : function()
6584     {
6585 //        Roo.log('ds onload');
6586         this.clear();
6587         
6588         var _this = this;
6589         var cm = this.cm;
6590         var ds = this.store;
6591         
6592         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6593             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6594             if (_this.store.sortInfo) {
6595                     
6596                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6597                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6598                 }
6599                 
6600                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6601                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6602                 }
6603             }
6604         });
6605         
6606         var tbody =  this.mainBody;
6607               
6608         if(ds.getCount() > 0){
6609             ds.data.each(function(d,rowIndex){
6610                 var row =  this.renderRow(cm, ds, rowIndex);
6611                 
6612                 tbody.createChild(row);
6613                 
6614                 var _this = this;
6615                 
6616                 if(row.cellObjects.length){
6617                     Roo.each(row.cellObjects, function(r){
6618                         _this.renderCellObject(r);
6619                     })
6620                 }
6621                 
6622             }, this);
6623         }
6624         
6625         var tfoot = this.el.select('tfoot', true).first();
6626         
6627         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6628             
6629             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6630             
6631             var total = this.ds.getTotalCount();
6632             
6633             if(this.footer.pageSize < total){
6634                 this.mainFoot.show();
6635             }
6636         }
6637         
6638         Roo.each(this.el.select('tbody td', true).elements, function(e){
6639             e.on('mouseover', _this.onMouseover, _this);
6640         });
6641         
6642         Roo.each(this.el.select('tbody td', true).elements, function(e){
6643             e.on('mouseout', _this.onMouseout, _this);
6644         });
6645         this.fireEvent('rowsrendered', this);
6646         
6647         this.autoSize();
6648     },
6649     
6650     
6651     onUpdate : function(ds,record)
6652     {
6653         this.refreshRow(record);
6654         this.autoSize();
6655     },
6656     
6657     onRemove : function(ds, record, index, isUpdate){
6658         if(isUpdate !== true){
6659             this.fireEvent("beforerowremoved", this, index, record);
6660         }
6661         var bt = this.mainBody.dom;
6662         
6663         var rows = this.el.select('tbody > tr', true).elements;
6664         
6665         if(typeof(rows[index]) != 'undefined'){
6666             bt.removeChild(rows[index].dom);
6667         }
6668         
6669 //        if(bt.rows[index]){
6670 //            bt.removeChild(bt.rows[index]);
6671 //        }
6672         
6673         if(isUpdate !== true){
6674             //this.stripeRows(index);
6675             //this.syncRowHeights(index, index);
6676             //this.layout();
6677             this.fireEvent("rowremoved", this, index, record);
6678         }
6679     },
6680     
6681     onAdd : function(ds, records, rowIndex)
6682     {
6683         //Roo.log('on Add called');
6684         // - note this does not handle multiple adding very well..
6685         var bt = this.mainBody.dom;
6686         for (var i =0 ; i < records.length;i++) {
6687             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6688             //Roo.log(records[i]);
6689             //Roo.log(this.store.getAt(rowIndex+i));
6690             this.insertRow(this.store, rowIndex + i, false);
6691             return;
6692         }
6693         
6694     },
6695     
6696     
6697     refreshRow : function(record){
6698         var ds = this.store, index;
6699         if(typeof record == 'number'){
6700             index = record;
6701             record = ds.getAt(index);
6702         }else{
6703             index = ds.indexOf(record);
6704         }
6705         this.insertRow(ds, index, true);
6706         this.autoSize();
6707         this.onRemove(ds, record, index+1, true);
6708         this.autoSize();
6709         //this.syncRowHeights(index, index);
6710         //this.layout();
6711         this.fireEvent("rowupdated", this, index, record);
6712     },
6713     
6714     insertRow : function(dm, rowIndex, isUpdate){
6715         
6716         if(!isUpdate){
6717             this.fireEvent("beforerowsinserted", this, rowIndex);
6718         }
6719             //var s = this.getScrollState();
6720         var row = this.renderRow(this.cm, this.store, rowIndex);
6721         // insert before rowIndex..
6722         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6723         
6724         var _this = this;
6725                 
6726         if(row.cellObjects.length){
6727             Roo.each(row.cellObjects, function(r){
6728                 _this.renderCellObject(r);
6729             })
6730         }
6731             
6732         if(!isUpdate){
6733             this.fireEvent("rowsinserted", this, rowIndex);
6734             //this.syncRowHeights(firstRow, lastRow);
6735             //this.stripeRows(firstRow);
6736             //this.layout();
6737         }
6738         
6739     },
6740     
6741     
6742     getRowDom : function(rowIndex)
6743     {
6744         var rows = this.el.select('tbody > tr', true).elements;
6745         
6746         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6747         
6748     },
6749     // returns the object tree for a tr..
6750   
6751     
6752     renderRow : function(cm, ds, rowIndex) 
6753     {
6754         var d = ds.getAt(rowIndex);
6755         
6756         var row = {
6757             tag : 'tr',
6758             cls : 'x-row-' + rowIndex,
6759             cn : []
6760         };
6761             
6762         var cellObjects = [];
6763         
6764         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6765             var config = cm.config[i];
6766             
6767             var renderer = cm.getRenderer(i);
6768             var value = '';
6769             var id = false;
6770             
6771             if(typeof(renderer) !== 'undefined'){
6772                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6773             }
6774             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6775             // and are rendered into the cells after the row is rendered - using the id for the element.
6776             
6777             if(typeof(value) === 'object'){
6778                 id = Roo.id();
6779                 cellObjects.push({
6780                     container : id,
6781                     cfg : value 
6782                 })
6783             }
6784             
6785             var rowcfg = {
6786                 record: d,
6787                 rowIndex : rowIndex,
6788                 colIndex : i,
6789                 rowClass : ''
6790             };
6791
6792             this.fireEvent('rowclass', this, rowcfg);
6793             
6794             var td = {
6795                 tag: 'td',
6796                 cls : rowcfg.rowClass + ' x-col-' + i,
6797                 style: '',
6798                 html: (typeof(value) === 'object') ? '' : value
6799             };
6800             
6801             if (id) {
6802                 td.id = id;
6803             }
6804             
6805             if(typeof(config.colspan) != 'undefined'){
6806                 td.colspan = config.colspan;
6807             }
6808             
6809             if(typeof(config.hidden) != 'undefined' && config.hidden){
6810                 td.style += ' display:none;';
6811             }
6812             
6813             if(typeof(config.align) != 'undefined' && config.align.length){
6814                 td.style += ' text-align:' + config.align + ';';
6815             }
6816             if(typeof(config.valign) != 'undefined' && config.valign.length){
6817                 td.style += ' vertical-align:' + config.valign + ';';
6818             }
6819             
6820             if(typeof(config.width) != 'undefined'){
6821                 td.style += ' width:' +  config.width + 'px;';
6822             }
6823             
6824             if(typeof(config.cursor) != 'undefined'){
6825                 td.style += ' cursor:' +  config.cursor + ';';
6826             }
6827             
6828             if(typeof(config.cls) != 'undefined'){
6829                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6830             }
6831             
6832             ['xs','sm','md','lg'].map(function(size){
6833                 
6834                 if(typeof(config[size]) == 'undefined'){
6835                     return;
6836                 }
6837                 
6838                 if (!config[size]) { // 0 = hidden
6839                     td.cls += ' hidden-' + size;
6840                     return;
6841                 }
6842                 
6843                 td.cls += ' col-' + size + '-' + config[size];
6844
6845             });
6846             
6847             row.cn.push(td);
6848            
6849         }
6850         
6851         row.cellObjects = cellObjects;
6852         
6853         return row;
6854           
6855     },
6856     
6857     
6858     
6859     onBeforeLoad : function()
6860     {
6861         
6862     },
6863      /**
6864      * Remove all rows
6865      */
6866     clear : function()
6867     {
6868         this.el.select('tbody', true).first().dom.innerHTML = '';
6869     },
6870     /**
6871      * Show or hide a row.
6872      * @param {Number} rowIndex to show or hide
6873      * @param {Boolean} state hide
6874      */
6875     setRowVisibility : function(rowIndex, state)
6876     {
6877         var bt = this.mainBody.dom;
6878         
6879         var rows = this.el.select('tbody > tr', true).elements;
6880         
6881         if(typeof(rows[rowIndex]) == 'undefined'){
6882             return;
6883         }
6884         rows[rowIndex].dom.style.display = state ? '' : 'none';
6885     },
6886     
6887     
6888     getSelectionModel : function(){
6889         if(!this.selModel){
6890             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6891         }
6892         return this.selModel;
6893     },
6894     /*
6895      * Render the Roo.bootstrap object from renderder
6896      */
6897     renderCellObject : function(r)
6898     {
6899         var _this = this;
6900         
6901         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6902         
6903         var t = r.cfg.render(r.container);
6904         
6905         if(r.cfg.cn){
6906             Roo.each(r.cfg.cn, function(c){
6907                 var child = {
6908                     container: t.getChildContainer(),
6909                     cfg: c
6910                 };
6911                 _this.renderCellObject(child);
6912             })
6913         }
6914     },
6915     
6916     getRowIndex : function(row)
6917     {
6918         var rowIndex = -1;
6919         
6920         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6921             if(el != row){
6922                 return;
6923             }
6924             
6925             rowIndex = index;
6926         });
6927         
6928         return rowIndex;
6929     },
6930      /**
6931      * Returns the grid's underlying element = used by panel.Grid
6932      * @return {Element} The element
6933      */
6934     getGridEl : function(){
6935         return this.el;
6936     },
6937      /**
6938      * Forces a resize - used by panel.Grid
6939      * @return {Element} The element
6940      */
6941     autoSize : function()
6942     {
6943         //var ctr = Roo.get(this.container.dom.parentElement);
6944         var ctr = Roo.get(this.el.dom);
6945         
6946         var thd = this.getGridEl().select('thead',true).first();
6947         var tbd = this.getGridEl().select('tbody', true).first();
6948         var tfd = this.getGridEl().select('tfoot', true).first();
6949         
6950         var cw = ctr.getWidth();
6951         
6952         if (tbd) {
6953             
6954             tbd.setSize(ctr.getWidth(),
6955                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6956             );
6957             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6958             cw -= barsize;
6959         }
6960         cw = Math.max(cw, this.totalWidth);
6961         this.getGridEl().select('tr',true).setWidth(cw);
6962         // resize 'expandable coloumn?
6963         
6964         return; // we doe not have a view in this design..
6965         
6966     },
6967     onBodyScroll: function()
6968     {
6969         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6970         if(this.mainHead){
6971             this.mainHead.setStyle({
6972                 'position' : 'relative',
6973                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6974             });
6975         }
6976         
6977         if(this.lazyLoad){
6978             
6979             var scrollHeight = this.mainBody.dom.scrollHeight;
6980             
6981             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6982             
6983             var height = this.mainBody.getHeight();
6984             
6985             if(scrollHeight - height == scrollTop) {
6986                 
6987                 var total = this.ds.getTotalCount();
6988                 
6989                 if(this.footer.cursor + this.footer.pageSize < total){
6990                     
6991                     this.footer.ds.load({
6992                         params : {
6993                             start : this.footer.cursor + this.footer.pageSize,
6994                             limit : this.footer.pageSize
6995                         },
6996                         add : true
6997                     });
6998                 }
6999             }
7000             
7001         }
7002     },
7003     
7004     onHeaderChange : function()
7005     {
7006         var header = this.renderHeader();
7007         var table = this.el.select('table', true).first();
7008         
7009         this.mainHead.remove();
7010         this.mainHead = table.createChild(header, this.mainBody, false);
7011     },
7012     
7013     onHiddenChange : function(colModel, colIndex, hidden)
7014     {
7015         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7016         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7017         
7018         this.CSS.updateRule(thSelector, "display", "");
7019         this.CSS.updateRule(tdSelector, "display", "");
7020         
7021         if(hidden){
7022             this.CSS.updateRule(thSelector, "display", "none");
7023             this.CSS.updateRule(tdSelector, "display", "none");
7024         }
7025         
7026         this.onHeaderChange();
7027         this.onLoad();
7028         
7029     }
7030     
7031 });
7032
7033  
7034
7035  /*
7036  * - LGPL
7037  *
7038  * table cell
7039  * 
7040  */
7041
7042 /**
7043  * @class Roo.bootstrap.TableCell
7044  * @extends Roo.bootstrap.Component
7045  * Bootstrap TableCell class
7046  * @cfg {String} html cell contain text
7047  * @cfg {String} cls cell class
7048  * @cfg {String} tag cell tag (td|th) default td
7049  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7050  * @cfg {String} align Aligns the content in a cell
7051  * @cfg {String} axis Categorizes cells
7052  * @cfg {String} bgcolor Specifies the background color of a cell
7053  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7054  * @cfg {Number} colspan Specifies the number of columns a cell should span
7055  * @cfg {String} headers Specifies one or more header cells a cell is related to
7056  * @cfg {Number} height Sets the height of a cell
7057  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7058  * @cfg {Number} rowspan Sets the number of rows a cell should span
7059  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7060  * @cfg {String} valign Vertical aligns the content in a cell
7061  * @cfg {Number} width Specifies the width of a cell
7062  * 
7063  * @constructor
7064  * Create a new TableCell
7065  * @param {Object} config The config object
7066  */
7067
7068 Roo.bootstrap.TableCell = function(config){
7069     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7070 };
7071
7072 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7073     
7074     html: false,
7075     cls: false,
7076     tag: false,
7077     abbr: false,
7078     align: false,
7079     axis: false,
7080     bgcolor: false,
7081     charoff: false,
7082     colspan: false,
7083     headers: false,
7084     height: false,
7085     nowrap: false,
7086     rowspan: false,
7087     scope: false,
7088     valign: false,
7089     width: false,
7090     
7091     
7092     getAutoCreate : function(){
7093         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7094         
7095         cfg = {
7096             tag: 'td'
7097         };
7098         
7099         if(this.tag){
7100             cfg.tag = this.tag;
7101         }
7102         
7103         if (this.html) {
7104             cfg.html=this.html
7105         }
7106         if (this.cls) {
7107             cfg.cls=this.cls
7108         }
7109         if (this.abbr) {
7110             cfg.abbr=this.abbr
7111         }
7112         if (this.align) {
7113             cfg.align=this.align
7114         }
7115         if (this.axis) {
7116             cfg.axis=this.axis
7117         }
7118         if (this.bgcolor) {
7119             cfg.bgcolor=this.bgcolor
7120         }
7121         if (this.charoff) {
7122             cfg.charoff=this.charoff
7123         }
7124         if (this.colspan) {
7125             cfg.colspan=this.colspan
7126         }
7127         if (this.headers) {
7128             cfg.headers=this.headers
7129         }
7130         if (this.height) {
7131             cfg.height=this.height
7132         }
7133         if (this.nowrap) {
7134             cfg.nowrap=this.nowrap
7135         }
7136         if (this.rowspan) {
7137             cfg.rowspan=this.rowspan
7138         }
7139         if (this.scope) {
7140             cfg.scope=this.scope
7141         }
7142         if (this.valign) {
7143             cfg.valign=this.valign
7144         }
7145         if (this.width) {
7146             cfg.width=this.width
7147         }
7148         
7149         
7150         return cfg;
7151     }
7152    
7153 });
7154
7155  
7156
7157  /*
7158  * - LGPL
7159  *
7160  * table row
7161  * 
7162  */
7163
7164 /**
7165  * @class Roo.bootstrap.TableRow
7166  * @extends Roo.bootstrap.Component
7167  * Bootstrap TableRow class
7168  * @cfg {String} cls row class
7169  * @cfg {String} align Aligns the content in a table row
7170  * @cfg {String} bgcolor Specifies a background color for a table row
7171  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7172  * @cfg {String} valign Vertical aligns the content in a table row
7173  * 
7174  * @constructor
7175  * Create a new TableRow
7176  * @param {Object} config The config object
7177  */
7178
7179 Roo.bootstrap.TableRow = function(config){
7180     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7181 };
7182
7183 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7184     
7185     cls: false,
7186     align: false,
7187     bgcolor: false,
7188     charoff: false,
7189     valign: false,
7190     
7191     getAutoCreate : function(){
7192         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7193         
7194         cfg = {
7195             tag: 'tr'
7196         };
7197             
7198         if(this.cls){
7199             cfg.cls = this.cls;
7200         }
7201         if(this.align){
7202             cfg.align = this.align;
7203         }
7204         if(this.bgcolor){
7205             cfg.bgcolor = this.bgcolor;
7206         }
7207         if(this.charoff){
7208             cfg.charoff = this.charoff;
7209         }
7210         if(this.valign){
7211             cfg.valign = this.valign;
7212         }
7213         
7214         return cfg;
7215     }
7216    
7217 });
7218
7219  
7220
7221  /*
7222  * - LGPL
7223  *
7224  * table body
7225  * 
7226  */
7227
7228 /**
7229  * @class Roo.bootstrap.TableBody
7230  * @extends Roo.bootstrap.Component
7231  * Bootstrap TableBody class
7232  * @cfg {String} cls element class
7233  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7234  * @cfg {String} align Aligns the content inside the element
7235  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7236  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7237  * 
7238  * @constructor
7239  * Create a new TableBody
7240  * @param {Object} config The config object
7241  */
7242
7243 Roo.bootstrap.TableBody = function(config){
7244     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7245 };
7246
7247 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7248     
7249     cls: false,
7250     tag: false,
7251     align: false,
7252     charoff: false,
7253     valign: false,
7254     
7255     getAutoCreate : function(){
7256         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7257         
7258         cfg = {
7259             tag: 'tbody'
7260         };
7261             
7262         if (this.cls) {
7263             cfg.cls=this.cls
7264         }
7265         if(this.tag){
7266             cfg.tag = this.tag;
7267         }
7268         
7269         if(this.align){
7270             cfg.align = this.align;
7271         }
7272         if(this.charoff){
7273             cfg.charoff = this.charoff;
7274         }
7275         if(this.valign){
7276             cfg.valign = this.valign;
7277         }
7278         
7279         return cfg;
7280     }
7281     
7282     
7283 //    initEvents : function()
7284 //    {
7285 //        
7286 //        if(!this.store){
7287 //            return;
7288 //        }
7289 //        
7290 //        this.store = Roo.factory(this.store, Roo.data);
7291 //        this.store.on('load', this.onLoad, this);
7292 //        
7293 //        this.store.load();
7294 //        
7295 //    },
7296 //    
7297 //    onLoad: function () 
7298 //    {   
7299 //        this.fireEvent('load', this);
7300 //    }
7301 //    
7302 //   
7303 });
7304
7305  
7306
7307  /*
7308  * Based on:
7309  * Ext JS Library 1.1.1
7310  * Copyright(c) 2006-2007, Ext JS, LLC.
7311  *
7312  * Originally Released Under LGPL - original licence link has changed is not relivant.
7313  *
7314  * Fork - LGPL
7315  * <script type="text/javascript">
7316  */
7317
7318 // as we use this in bootstrap.
7319 Roo.namespace('Roo.form');
7320  /**
7321  * @class Roo.form.Action
7322  * Internal Class used to handle form actions
7323  * @constructor
7324  * @param {Roo.form.BasicForm} el The form element or its id
7325  * @param {Object} config Configuration options
7326  */
7327
7328  
7329  
7330 // define the action interface
7331 Roo.form.Action = function(form, options){
7332     this.form = form;
7333     this.options = options || {};
7334 };
7335 /**
7336  * Client Validation Failed
7337  * @const 
7338  */
7339 Roo.form.Action.CLIENT_INVALID = 'client';
7340 /**
7341  * Server Validation Failed
7342  * @const 
7343  */
7344 Roo.form.Action.SERVER_INVALID = 'server';
7345  /**
7346  * Connect to Server Failed
7347  * @const 
7348  */
7349 Roo.form.Action.CONNECT_FAILURE = 'connect';
7350 /**
7351  * Reading Data from Server Failed
7352  * @const 
7353  */
7354 Roo.form.Action.LOAD_FAILURE = 'load';
7355
7356 Roo.form.Action.prototype = {
7357     type : 'default',
7358     failureType : undefined,
7359     response : undefined,
7360     result : undefined,
7361
7362     // interface method
7363     run : function(options){
7364
7365     },
7366
7367     // interface method
7368     success : function(response){
7369
7370     },
7371
7372     // interface method
7373     handleResponse : function(response){
7374
7375     },
7376
7377     // default connection failure
7378     failure : function(response){
7379         
7380         this.response = response;
7381         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7382         this.form.afterAction(this, false);
7383     },
7384
7385     processResponse : function(response){
7386         this.response = response;
7387         if(!response.responseText){
7388             return true;
7389         }
7390         this.result = this.handleResponse(response);
7391         return this.result;
7392     },
7393
7394     // utility functions used internally
7395     getUrl : function(appendParams){
7396         var url = this.options.url || this.form.url || this.form.el.dom.action;
7397         if(appendParams){
7398             var p = this.getParams();
7399             if(p){
7400                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7401             }
7402         }
7403         return url;
7404     },
7405
7406     getMethod : function(){
7407         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7408     },
7409
7410     getParams : function(){
7411         var bp = this.form.baseParams;
7412         var p = this.options.params;
7413         if(p){
7414             if(typeof p == "object"){
7415                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7416             }else if(typeof p == 'string' && bp){
7417                 p += '&' + Roo.urlEncode(bp);
7418             }
7419         }else if(bp){
7420             p = Roo.urlEncode(bp);
7421         }
7422         return p;
7423     },
7424
7425     createCallback : function(){
7426         return {
7427             success: this.success,
7428             failure: this.failure,
7429             scope: this,
7430             timeout: (this.form.timeout*1000),
7431             upload: this.form.fileUpload ? this.success : undefined
7432         };
7433     }
7434 };
7435
7436 Roo.form.Action.Submit = function(form, options){
7437     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7438 };
7439
7440 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7441     type : 'submit',
7442
7443     haveProgress : false,
7444     uploadComplete : false,
7445     
7446     // uploadProgress indicator.
7447     uploadProgress : function()
7448     {
7449         if (!this.form.progressUrl) {
7450             return;
7451         }
7452         
7453         if (!this.haveProgress) {
7454             Roo.MessageBox.progress("Uploading", "Uploading");
7455         }
7456         if (this.uploadComplete) {
7457            Roo.MessageBox.hide();
7458            return;
7459         }
7460         
7461         this.haveProgress = true;
7462    
7463         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7464         
7465         var c = new Roo.data.Connection();
7466         c.request({
7467             url : this.form.progressUrl,
7468             params: {
7469                 id : uid
7470             },
7471             method: 'GET',
7472             success : function(req){
7473                //console.log(data);
7474                 var rdata = false;
7475                 var edata;
7476                 try  {
7477                    rdata = Roo.decode(req.responseText)
7478                 } catch (e) {
7479                     Roo.log("Invalid data from server..");
7480                     Roo.log(edata);
7481                     return;
7482                 }
7483                 if (!rdata || !rdata.success) {
7484                     Roo.log(rdata);
7485                     Roo.MessageBox.alert(Roo.encode(rdata));
7486                     return;
7487                 }
7488                 var data = rdata.data;
7489                 
7490                 if (this.uploadComplete) {
7491                    Roo.MessageBox.hide();
7492                    return;
7493                 }
7494                    
7495                 if (data){
7496                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7497                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7498                     );
7499                 }
7500                 this.uploadProgress.defer(2000,this);
7501             },
7502        
7503             failure: function(data) {
7504                 Roo.log('progress url failed ');
7505                 Roo.log(data);
7506             },
7507             scope : this
7508         });
7509            
7510     },
7511     
7512     
7513     run : function()
7514     {
7515         // run get Values on the form, so it syncs any secondary forms.
7516         this.form.getValues();
7517         
7518         var o = this.options;
7519         var method = this.getMethod();
7520         var isPost = method == 'POST';
7521         if(o.clientValidation === false || this.form.isValid()){
7522             
7523             if (this.form.progressUrl) {
7524                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7525                     (new Date() * 1) + '' + Math.random());
7526                     
7527             } 
7528             
7529             
7530             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7531                 form:this.form.el.dom,
7532                 url:this.getUrl(!isPost),
7533                 method: method,
7534                 params:isPost ? this.getParams() : null,
7535                 isUpload: this.form.fileUpload
7536             }));
7537             
7538             this.uploadProgress();
7539
7540         }else if (o.clientValidation !== false){ // client validation failed
7541             this.failureType = Roo.form.Action.CLIENT_INVALID;
7542             this.form.afterAction(this, false);
7543         }
7544     },
7545
7546     success : function(response)
7547     {
7548         this.uploadComplete= true;
7549         if (this.haveProgress) {
7550             Roo.MessageBox.hide();
7551         }
7552         
7553         
7554         var result = this.processResponse(response);
7555         if(result === true || result.success){
7556             this.form.afterAction(this, true);
7557             return;
7558         }
7559         if(result.errors){
7560             this.form.markInvalid(result.errors);
7561             this.failureType = Roo.form.Action.SERVER_INVALID;
7562         }
7563         this.form.afterAction(this, false);
7564     },
7565     failure : function(response)
7566     {
7567         this.uploadComplete= true;
7568         if (this.haveProgress) {
7569             Roo.MessageBox.hide();
7570         }
7571         
7572         this.response = response;
7573         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7574         this.form.afterAction(this, false);
7575     },
7576     
7577     handleResponse : function(response){
7578         if(this.form.errorReader){
7579             var rs = this.form.errorReader.read(response);
7580             var errors = [];
7581             if(rs.records){
7582                 for(var i = 0, len = rs.records.length; i < len; i++) {
7583                     var r = rs.records[i];
7584                     errors[i] = r.data;
7585                 }
7586             }
7587             if(errors.length < 1){
7588                 errors = null;
7589             }
7590             return {
7591                 success : rs.success,
7592                 errors : errors
7593             };
7594         }
7595         var ret = false;
7596         try {
7597             ret = Roo.decode(response.responseText);
7598         } catch (e) {
7599             ret = {
7600                 success: false,
7601                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7602                 errors : []
7603             };
7604         }
7605         return ret;
7606         
7607     }
7608 });
7609
7610
7611 Roo.form.Action.Load = function(form, options){
7612     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7613     this.reader = this.form.reader;
7614 };
7615
7616 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7617     type : 'load',
7618
7619     run : function(){
7620         
7621         Roo.Ajax.request(Roo.apply(
7622                 this.createCallback(), {
7623                     method:this.getMethod(),
7624                     url:this.getUrl(false),
7625                     params:this.getParams()
7626         }));
7627     },
7628
7629     success : function(response){
7630         
7631         var result = this.processResponse(response);
7632         if(result === true || !result.success || !result.data){
7633             this.failureType = Roo.form.Action.LOAD_FAILURE;
7634             this.form.afterAction(this, false);
7635             return;
7636         }
7637         this.form.clearInvalid();
7638         this.form.setValues(result.data);
7639         this.form.afterAction(this, true);
7640     },
7641
7642     handleResponse : function(response){
7643         if(this.form.reader){
7644             var rs = this.form.reader.read(response);
7645             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7646             return {
7647                 success : rs.success,
7648                 data : data
7649             };
7650         }
7651         return Roo.decode(response.responseText);
7652     }
7653 });
7654
7655 Roo.form.Action.ACTION_TYPES = {
7656     'load' : Roo.form.Action.Load,
7657     'submit' : Roo.form.Action.Submit
7658 };/*
7659  * - LGPL
7660  *
7661  * form
7662  *
7663  */
7664
7665 /**
7666  * @class Roo.bootstrap.Form
7667  * @extends Roo.bootstrap.Component
7668  * Bootstrap Form class
7669  * @cfg {String} method  GET | POST (default POST)
7670  * @cfg {String} labelAlign top | left (default top)
7671  * @cfg {String} align left  | right - for navbars
7672  * @cfg {Boolean} loadMask load mask when submit (default true)
7673
7674  *
7675  * @constructor
7676  * Create a new Form
7677  * @param {Object} config The config object
7678  */
7679
7680
7681 Roo.bootstrap.Form = function(config){
7682     
7683     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7684     
7685     Roo.bootstrap.Form.popover.apply();
7686     
7687     this.addEvents({
7688         /**
7689          * @event clientvalidation
7690          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7691          * @param {Form} this
7692          * @param {Boolean} valid true if the form has passed client-side validation
7693          */
7694         clientvalidation: true,
7695         /**
7696          * @event beforeaction
7697          * Fires before any action is performed. Return false to cancel the action.
7698          * @param {Form} this
7699          * @param {Action} action The action to be performed
7700          */
7701         beforeaction: true,
7702         /**
7703          * @event actionfailed
7704          * Fires when an action fails.
7705          * @param {Form} this
7706          * @param {Action} action The action that failed
7707          */
7708         actionfailed : true,
7709         /**
7710          * @event actioncomplete
7711          * Fires when an action is completed.
7712          * @param {Form} this
7713          * @param {Action} action The action that completed
7714          */
7715         actioncomplete : true
7716     });
7717 };
7718
7719 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7720
7721      /**
7722      * @cfg {String} method
7723      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7724      */
7725     method : 'POST',
7726     /**
7727      * @cfg {String} url
7728      * The URL to use for form actions if one isn't supplied in the action options.
7729      */
7730     /**
7731      * @cfg {Boolean} fileUpload
7732      * Set to true if this form is a file upload.
7733      */
7734
7735     /**
7736      * @cfg {Object} baseParams
7737      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7738      */
7739
7740     /**
7741      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7742      */
7743     timeout: 30,
7744     /**
7745      * @cfg {Sting} align (left|right) for navbar forms
7746      */
7747     align : 'left',
7748
7749     // private
7750     activeAction : null,
7751
7752     /**
7753      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7754      * element by passing it or its id or mask the form itself by passing in true.
7755      * @type Mixed
7756      */
7757     waitMsgTarget : false,
7758
7759     loadMask : true,
7760     
7761     /**
7762      * @cfg {Boolean} errorMask (true|false) default false
7763      */
7764     errorMask : false,
7765     
7766     /**
7767      * @cfg {Number} maskOffset Default 100
7768      */
7769     maskOffset : 100,
7770     
7771     /**
7772      * @cfg {Boolean} maskBody
7773      */
7774     maskBody : false,
7775
7776     getAutoCreate : function(){
7777
7778         var cfg = {
7779             tag: 'form',
7780             method : this.method || 'POST',
7781             id : this.id || Roo.id(),
7782             cls : ''
7783         };
7784         if (this.parent().xtype.match(/^Nav/)) {
7785             cfg.cls = 'navbar-form navbar-' + this.align;
7786
7787         }
7788
7789         if (this.labelAlign == 'left' ) {
7790             cfg.cls += ' form-horizontal';
7791         }
7792
7793
7794         return cfg;
7795     },
7796     initEvents : function()
7797     {
7798         this.el.on('submit', this.onSubmit, this);
7799         // this was added as random key presses on the form where triggering form submit.
7800         this.el.on('keypress', function(e) {
7801             if (e.getCharCode() != 13) {
7802                 return true;
7803             }
7804             // we might need to allow it for textareas.. and some other items.
7805             // check e.getTarget().
7806
7807             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7808                 return true;
7809             }
7810
7811             Roo.log("keypress blocked");
7812
7813             e.preventDefault();
7814             return false;
7815         });
7816         
7817     },
7818     // private
7819     onSubmit : function(e){
7820         e.stopEvent();
7821     },
7822
7823      /**
7824      * Returns true if client-side validation on the form is successful.
7825      * @return Boolean
7826      */
7827     isValid : function(){
7828         var items = this.getItems();
7829         var valid = true;
7830         var target = false;
7831         
7832         items.each(function(f){
7833             
7834             if(f.validate()){
7835                 return;
7836             }
7837             
7838             valid = false;
7839
7840             if(!target && f.el.isVisible(true)){
7841                 target = f;
7842             }
7843            
7844         });
7845         
7846         if(this.errorMask && !valid){
7847             Roo.bootstrap.Form.popover.mask(this, target);
7848         }
7849         
7850         return valid;
7851     },
7852     
7853     /**
7854      * Returns true if any fields in this form have changed since their original load.
7855      * @return Boolean
7856      */
7857     isDirty : function(){
7858         var dirty = false;
7859         var items = this.getItems();
7860         items.each(function(f){
7861            if(f.isDirty()){
7862                dirty = true;
7863                return false;
7864            }
7865            return true;
7866         });
7867         return dirty;
7868     },
7869      /**
7870      * Performs a predefined action (submit or load) or custom actions you define on this form.
7871      * @param {String} actionName The name of the action type
7872      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7873      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7874      * accept other config options):
7875      * <pre>
7876 Property          Type             Description
7877 ----------------  ---------------  ----------------------------------------------------------------------------------
7878 url               String           The url for the action (defaults to the form's url)
7879 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7880 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7881 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7882                                    validate the form on the client (defaults to false)
7883      * </pre>
7884      * @return {BasicForm} this
7885      */
7886     doAction : function(action, options){
7887         if(typeof action == 'string'){
7888             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7889         }
7890         if(this.fireEvent('beforeaction', this, action) !== false){
7891             this.beforeAction(action);
7892             action.run.defer(100, action);
7893         }
7894         return this;
7895     },
7896
7897     // private
7898     beforeAction : function(action){
7899         var o = action.options;
7900         
7901         if(this.loadMask){
7902             
7903             if(this.maskBody){
7904                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7905             } else {
7906                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7907             }
7908         }
7909         // not really supported yet.. ??
7910
7911         //if(this.waitMsgTarget === true){
7912         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7913         //}else if(this.waitMsgTarget){
7914         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7915         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7916         //}else {
7917         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7918        // }
7919
7920     },
7921
7922     // private
7923     afterAction : function(action, success){
7924         this.activeAction = null;
7925         var o = action.options;
7926
7927         if(this.loadMask){
7928             
7929             if(this.maskBody){
7930                 Roo.get(document.body).unmask();
7931             } else {
7932                 this.el.unmask();
7933             }
7934         }
7935         
7936         //if(this.waitMsgTarget === true){
7937 //            this.el.unmask();
7938         //}else if(this.waitMsgTarget){
7939         //    this.waitMsgTarget.unmask();
7940         //}else{
7941         //    Roo.MessageBox.updateProgress(1);
7942         //    Roo.MessageBox.hide();
7943        // }
7944         //
7945         if(success){
7946             if(o.reset){
7947                 this.reset();
7948             }
7949             Roo.callback(o.success, o.scope, [this, action]);
7950             this.fireEvent('actioncomplete', this, action);
7951
7952         }else{
7953
7954             // failure condition..
7955             // we have a scenario where updates need confirming.
7956             // eg. if a locking scenario exists..
7957             // we look for { errors : { needs_confirm : true }} in the response.
7958             if (
7959                 (typeof(action.result) != 'undefined')  &&
7960                 (typeof(action.result.errors) != 'undefined')  &&
7961                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7962            ){
7963                 var _t = this;
7964                 Roo.log("not supported yet");
7965                  /*
7966
7967                 Roo.MessageBox.confirm(
7968                     "Change requires confirmation",
7969                     action.result.errorMsg,
7970                     function(r) {
7971                         if (r != 'yes') {
7972                             return;
7973                         }
7974                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7975                     }
7976
7977                 );
7978                 */
7979
7980
7981                 return;
7982             }
7983
7984             Roo.callback(o.failure, o.scope, [this, action]);
7985             // show an error message if no failed handler is set..
7986             if (!this.hasListener('actionfailed')) {
7987                 Roo.log("need to add dialog support");
7988                 /*
7989                 Roo.MessageBox.alert("Error",
7990                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7991                         action.result.errorMsg :
7992                         "Saving Failed, please check your entries or try again"
7993                 );
7994                 */
7995             }
7996
7997             this.fireEvent('actionfailed', this, action);
7998         }
7999
8000     },
8001     /**
8002      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8003      * @param {String} id The value to search for
8004      * @return Field
8005      */
8006     findField : function(id){
8007         var items = this.getItems();
8008         var field = items.get(id);
8009         if(!field){
8010              items.each(function(f){
8011                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8012                     field = f;
8013                     return false;
8014                 }
8015                 return true;
8016             });
8017         }
8018         return field || null;
8019     },
8020      /**
8021      * Mark fields in this form invalid in bulk.
8022      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8023      * @return {BasicForm} this
8024      */
8025     markInvalid : function(errors){
8026         if(errors instanceof Array){
8027             for(var i = 0, len = errors.length; i < len; i++){
8028                 var fieldError = errors[i];
8029                 var f = this.findField(fieldError.id);
8030                 if(f){
8031                     f.markInvalid(fieldError.msg);
8032                 }
8033             }
8034         }else{
8035             var field, id;
8036             for(id in errors){
8037                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8038                     field.markInvalid(errors[id]);
8039                 }
8040             }
8041         }
8042         //Roo.each(this.childForms || [], function (f) {
8043         //    f.markInvalid(errors);
8044         //});
8045
8046         return this;
8047     },
8048
8049     /**
8050      * Set values for fields in this form in bulk.
8051      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8052      * @return {BasicForm} this
8053      */
8054     setValues : function(values){
8055         if(values instanceof Array){ // array of objects
8056             for(var i = 0, len = values.length; i < len; i++){
8057                 var v = values[i];
8058                 var f = this.findField(v.id);
8059                 if(f){
8060                     f.setValue(v.value);
8061                     if(this.trackResetOnLoad){
8062                         f.originalValue = f.getValue();
8063                     }
8064                 }
8065             }
8066         }else{ // object hash
8067             var field, id;
8068             for(id in values){
8069                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8070
8071                     if (field.setFromData &&
8072                         field.valueField &&
8073                         field.displayField &&
8074                         // combos' with local stores can
8075                         // be queried via setValue()
8076                         // to set their value..
8077                         (field.store && !field.store.isLocal)
8078                         ) {
8079                         // it's a combo
8080                         var sd = { };
8081                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8082                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8083                         field.setFromData(sd);
8084
8085                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8086                         
8087                         field.setFromData(values);
8088                         
8089                     } else {
8090                         field.setValue(values[id]);
8091                     }
8092
8093
8094                     if(this.trackResetOnLoad){
8095                         field.originalValue = field.getValue();
8096                     }
8097                 }
8098             }
8099         }
8100
8101         //Roo.each(this.childForms || [], function (f) {
8102         //    f.setValues(values);
8103         //});
8104
8105         return this;
8106     },
8107
8108     /**
8109      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8110      * they are returned as an array.
8111      * @param {Boolean} asString
8112      * @return {Object}
8113      */
8114     getValues : function(asString){
8115         //if (this.childForms) {
8116             // copy values from the child forms
8117         //    Roo.each(this.childForms, function (f) {
8118         //        this.setValues(f.getValues());
8119         //    }, this);
8120         //}
8121
8122
8123
8124         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8125         if(asString === true){
8126             return fs;
8127         }
8128         return Roo.urlDecode(fs);
8129     },
8130
8131     /**
8132      * Returns the fields in this form as an object with key/value pairs.
8133      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8134      * @return {Object}
8135      */
8136     getFieldValues : function(with_hidden)
8137     {
8138         var items = this.getItems();
8139         var ret = {};
8140         items.each(function(f){
8141             
8142             if (!f.getName()) {
8143                 return;
8144             }
8145             
8146             var v = f.getValue();
8147             
8148             if (f.inputType =='radio') {
8149                 if (typeof(ret[f.getName()]) == 'undefined') {
8150                     ret[f.getName()] = ''; // empty..
8151                 }
8152
8153                 if (!f.el.dom.checked) {
8154                     return;
8155
8156                 }
8157                 v = f.el.dom.value;
8158
8159             }
8160             
8161             if(f.xtype == 'MoneyField'){
8162                 ret[f.currencyName] = f.getCurrency();
8163             }
8164
8165             // not sure if this supported any more..
8166             if ((typeof(v) == 'object') && f.getRawValue) {
8167                 v = f.getRawValue() ; // dates..
8168             }
8169             // combo boxes where name != hiddenName...
8170             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8171                 ret[f.name] = f.getRawValue();
8172             }
8173             ret[f.getName()] = v;
8174         });
8175
8176         return ret;
8177     },
8178
8179     /**
8180      * Clears all invalid messages in this form.
8181      * @return {BasicForm} this
8182      */
8183     clearInvalid : function(){
8184         var items = this.getItems();
8185
8186         items.each(function(f){
8187            f.clearInvalid();
8188         });
8189
8190         return this;
8191     },
8192
8193     /**
8194      * Resets this form.
8195      * @return {BasicForm} this
8196      */
8197     reset : function(){
8198         var items = this.getItems();
8199         items.each(function(f){
8200             f.reset();
8201         });
8202
8203         Roo.each(this.childForms || [], function (f) {
8204             f.reset();
8205         });
8206
8207
8208         return this;
8209     },
8210     
8211     getItems : function()
8212     {
8213         var r=new Roo.util.MixedCollection(false, function(o){
8214             return o.id || (o.id = Roo.id());
8215         });
8216         var iter = function(el) {
8217             if (el.inputEl) {
8218                 r.add(el);
8219             }
8220             if (!el.items) {
8221                 return;
8222             }
8223             Roo.each(el.items,function(e) {
8224                 iter(e);
8225             });
8226         };
8227
8228         iter(this);
8229         return r;
8230     },
8231     
8232     hideFields : function(items)
8233     {
8234         Roo.each(items, function(i){
8235             
8236             var f = this.findField(i);
8237             
8238             if(!f){
8239                 return;
8240             }
8241             
8242             if(f.xtype == 'DateField'){
8243                 f.setVisible(false);
8244                 return;
8245             }
8246             
8247             f.hide();
8248             
8249         }, this);
8250     },
8251     
8252     showFields : function(items)
8253     {
8254         Roo.each(items, function(i){
8255             
8256             var f = this.findField(i);
8257             
8258             if(!f){
8259                 return;
8260             }
8261             
8262             if(f.xtype == 'DateField'){
8263                 f.setVisible(true);
8264                 return;
8265             }
8266             
8267             f.show();
8268             
8269         }, this);
8270     }
8271
8272 });
8273
8274 Roo.apply(Roo.bootstrap.Form, {
8275     
8276     popover : {
8277         
8278         padding : 5,
8279         
8280         isApplied : false,
8281         
8282         isMasked : false,
8283         
8284         form : false,
8285         
8286         target : false,
8287         
8288         toolTip : false,
8289         
8290         intervalID : false,
8291         
8292         maskEl : false,
8293         
8294         apply : function()
8295         {
8296             if(this.isApplied){
8297                 return;
8298             }
8299             
8300             this.maskEl = {
8301                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8302                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8303                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8304                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8305             };
8306             
8307             this.maskEl.top.enableDisplayMode("block");
8308             this.maskEl.left.enableDisplayMode("block");
8309             this.maskEl.bottom.enableDisplayMode("block");
8310             this.maskEl.right.enableDisplayMode("block");
8311             
8312             this.toolTip = new Roo.bootstrap.Tooltip({
8313                 cls : 'roo-form-error-popover',
8314                 alignment : {
8315                     'left' : ['r-l', [-2,0], 'right'],
8316                     'right' : ['l-r', [2,0], 'left'],
8317                     'bottom' : ['tl-bl', [0,2], 'top'],
8318                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8319                 }
8320             });
8321             
8322             this.toolTip.render(Roo.get(document.body));
8323
8324             this.toolTip.el.enableDisplayMode("block");
8325             
8326             Roo.get(document.body).on('click', function(){
8327                 this.unmask();
8328             }, this);
8329             
8330             Roo.get(document.body).on('touchstart', function(){
8331                 this.unmask();
8332             }, this);
8333             
8334             this.isApplied = true
8335         },
8336         
8337         mask : function(form, target)
8338         {
8339             this.form = form;
8340             
8341             this.target = target;
8342             
8343             if(!this.form.errorMask || !target.el){
8344                 return;
8345             }
8346             
8347             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8348             
8349             Roo.log(scrollable);
8350             
8351             var ot = this.target.el.calcOffsetsTo(scrollable);
8352             
8353             var scrollTo = ot[1] - this.form.maskOffset;
8354             
8355             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8356             
8357             scrollable.scrollTo('top', scrollTo);
8358             
8359             var box = this.target.el.getBox();
8360             Roo.log(box);
8361             var zIndex = Roo.bootstrap.Modal.zIndex++;
8362
8363             
8364             this.maskEl.top.setStyle('position', 'absolute');
8365             this.maskEl.top.setStyle('z-index', zIndex);
8366             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8367             this.maskEl.top.setLeft(0);
8368             this.maskEl.top.setTop(0);
8369             this.maskEl.top.show();
8370             
8371             this.maskEl.left.setStyle('position', 'absolute');
8372             this.maskEl.left.setStyle('z-index', zIndex);
8373             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8374             this.maskEl.left.setLeft(0);
8375             this.maskEl.left.setTop(box.y - this.padding);
8376             this.maskEl.left.show();
8377
8378             this.maskEl.bottom.setStyle('position', 'absolute');
8379             this.maskEl.bottom.setStyle('z-index', zIndex);
8380             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8381             this.maskEl.bottom.setLeft(0);
8382             this.maskEl.bottom.setTop(box.bottom + this.padding);
8383             this.maskEl.bottom.show();
8384
8385             this.maskEl.right.setStyle('position', 'absolute');
8386             this.maskEl.right.setStyle('z-index', zIndex);
8387             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8388             this.maskEl.right.setLeft(box.right + this.padding);
8389             this.maskEl.right.setTop(box.y - this.padding);
8390             this.maskEl.right.show();
8391
8392             this.toolTip.bindEl = this.target.el;
8393
8394             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8395
8396             var tip = this.target.blankText;
8397
8398             if(this.target.getValue() !== '' ) {
8399                 
8400                 if (this.target.invalidText.length) {
8401                     tip = this.target.invalidText;
8402                 } else if (this.target.regexText.length){
8403                     tip = this.target.regexText;
8404                 }
8405             }
8406
8407             this.toolTip.show(tip);
8408
8409             this.intervalID = window.setInterval(function() {
8410                 Roo.bootstrap.Form.popover.unmask();
8411             }, 10000);
8412
8413             window.onwheel = function(){ return false;};
8414             
8415             (function(){ this.isMasked = true; }).defer(500, this);
8416             
8417         },
8418         
8419         unmask : function()
8420         {
8421             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8422                 return;
8423             }
8424             
8425             this.maskEl.top.setStyle('position', 'absolute');
8426             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8427             this.maskEl.top.hide();
8428
8429             this.maskEl.left.setStyle('position', 'absolute');
8430             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8431             this.maskEl.left.hide();
8432
8433             this.maskEl.bottom.setStyle('position', 'absolute');
8434             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8435             this.maskEl.bottom.hide();
8436
8437             this.maskEl.right.setStyle('position', 'absolute');
8438             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8439             this.maskEl.right.hide();
8440             
8441             this.toolTip.hide();
8442             
8443             this.toolTip.el.hide();
8444             
8445             window.onwheel = function(){ return true;};
8446             
8447             if(this.intervalID){
8448                 window.clearInterval(this.intervalID);
8449                 this.intervalID = false;
8450             }
8451             
8452             this.isMasked = false;
8453             
8454         }
8455         
8456     }
8457     
8458 });
8459
8460 /*
8461  * Based on:
8462  * Ext JS Library 1.1.1
8463  * Copyright(c) 2006-2007, Ext JS, LLC.
8464  *
8465  * Originally Released Under LGPL - original licence link has changed is not relivant.
8466  *
8467  * Fork - LGPL
8468  * <script type="text/javascript">
8469  */
8470 /**
8471  * @class Roo.form.VTypes
8472  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8473  * @singleton
8474  */
8475 Roo.form.VTypes = function(){
8476     // closure these in so they are only created once.
8477     var alpha = /^[a-zA-Z_]+$/;
8478     var alphanum = /^[a-zA-Z0-9_]+$/;
8479     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8480     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8481
8482     // All these messages and functions are configurable
8483     return {
8484         /**
8485          * The function used to validate email addresses
8486          * @param {String} value The email address
8487          */
8488         'email' : function(v){
8489             return email.test(v);
8490         },
8491         /**
8492          * The error text to display when the email validation function returns false
8493          * @type String
8494          */
8495         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8496         /**
8497          * The keystroke filter mask to be applied on email input
8498          * @type RegExp
8499          */
8500         'emailMask' : /[a-z0-9_\.\-@]/i,
8501
8502         /**
8503          * The function used to validate URLs
8504          * @param {String} value The URL
8505          */
8506         'url' : function(v){
8507             return url.test(v);
8508         },
8509         /**
8510          * The error text to display when the url validation function returns false
8511          * @type String
8512          */
8513         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8514         
8515         /**
8516          * The function used to validate alpha values
8517          * @param {String} value The value
8518          */
8519         'alpha' : function(v){
8520             return alpha.test(v);
8521         },
8522         /**
8523          * The error text to display when the alpha validation function returns false
8524          * @type String
8525          */
8526         'alphaText' : 'This field should only contain letters and _',
8527         /**
8528          * The keystroke filter mask to be applied on alpha input
8529          * @type RegExp
8530          */
8531         'alphaMask' : /[a-z_]/i,
8532
8533         /**
8534          * The function used to validate alphanumeric values
8535          * @param {String} value The value
8536          */
8537         'alphanum' : function(v){
8538             return alphanum.test(v);
8539         },
8540         /**
8541          * The error text to display when the alphanumeric validation function returns false
8542          * @type String
8543          */
8544         'alphanumText' : 'This field should only contain letters, numbers and _',
8545         /**
8546          * The keystroke filter mask to be applied on alphanumeric input
8547          * @type RegExp
8548          */
8549         'alphanumMask' : /[a-z0-9_]/i
8550     };
8551 }();/*
8552  * - LGPL
8553  *
8554  * Input
8555  * 
8556  */
8557
8558 /**
8559  * @class Roo.bootstrap.Input
8560  * @extends Roo.bootstrap.Component
8561  * Bootstrap Input class
8562  * @cfg {Boolean} disabled is it disabled
8563  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8564  * @cfg {String} name name of the input
8565  * @cfg {string} fieldLabel - the label associated
8566  * @cfg {string} placeholder - placeholder to put in text.
8567  * @cfg {string}  before - input group add on before
8568  * @cfg {string} after - input group add on after
8569  * @cfg {string} size - (lg|sm) or leave empty..
8570  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8571  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8572  * @cfg {Number} md colspan out of 12 for computer-sized screens
8573  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8574  * @cfg {string} value default value of the input
8575  * @cfg {Number} labelWidth set the width of label 
8576  * @cfg {Number} labellg set the width of label (1-12)
8577  * @cfg {Number} labelmd set the width of label (1-12)
8578  * @cfg {Number} labelsm set the width of label (1-12)
8579  * @cfg {Number} labelxs set the width of label (1-12)
8580  * @cfg {String} labelAlign (top|left)
8581  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8582  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8583  * @cfg {String} indicatorpos (left|right) default left
8584  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8585  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8586
8587  * @cfg {String} align (left|center|right) Default left
8588  * @cfg {Boolean} forceFeedback (true|false) Default false
8589  * 
8590  * @constructor
8591  * Create a new Input
8592  * @param {Object} config The config object
8593  */
8594
8595 Roo.bootstrap.Input = function(config){
8596     
8597     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8598     
8599     this.addEvents({
8600         /**
8601          * @event focus
8602          * Fires when this field receives input focus.
8603          * @param {Roo.form.Field} this
8604          */
8605         focus : true,
8606         /**
8607          * @event blur
8608          * Fires when this field loses input focus.
8609          * @param {Roo.form.Field} this
8610          */
8611         blur : true,
8612         /**
8613          * @event specialkey
8614          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8615          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8616          * @param {Roo.form.Field} this
8617          * @param {Roo.EventObject} e The event object
8618          */
8619         specialkey : true,
8620         /**
8621          * @event change
8622          * Fires just before the field blurs if the field value has changed.
8623          * @param {Roo.form.Field} this
8624          * @param {Mixed} newValue The new value
8625          * @param {Mixed} oldValue The original value
8626          */
8627         change : true,
8628         /**
8629          * @event invalid
8630          * Fires after the field has been marked as invalid.
8631          * @param {Roo.form.Field} this
8632          * @param {String} msg The validation message
8633          */
8634         invalid : true,
8635         /**
8636          * @event valid
8637          * Fires after the field has been validated with no errors.
8638          * @param {Roo.form.Field} this
8639          */
8640         valid : true,
8641          /**
8642          * @event keyup
8643          * Fires after the key up
8644          * @param {Roo.form.Field} this
8645          * @param {Roo.EventObject}  e The event Object
8646          */
8647         keyup : true
8648     });
8649 };
8650
8651 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8652      /**
8653      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8654       automatic validation (defaults to "keyup").
8655      */
8656     validationEvent : "keyup",
8657      /**
8658      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8659      */
8660     validateOnBlur : true,
8661     /**
8662      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8663      */
8664     validationDelay : 250,
8665      /**
8666      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8667      */
8668     focusClass : "x-form-focus",  // not needed???
8669     
8670        
8671     /**
8672      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8673      */
8674     invalidClass : "has-warning",
8675     
8676     /**
8677      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8678      */
8679     validClass : "has-success",
8680     
8681     /**
8682      * @cfg {Boolean} hasFeedback (true|false) default true
8683      */
8684     hasFeedback : true,
8685     
8686     /**
8687      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8688      */
8689     invalidFeedbackClass : "glyphicon-warning-sign",
8690     
8691     /**
8692      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8693      */
8694     validFeedbackClass : "glyphicon-ok",
8695     
8696     /**
8697      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8698      */
8699     selectOnFocus : false,
8700     
8701      /**
8702      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8703      */
8704     maskRe : null,
8705        /**
8706      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8707      */
8708     vtype : null,
8709     
8710       /**
8711      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8712      */
8713     disableKeyFilter : false,
8714     
8715        /**
8716      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8717      */
8718     disabled : false,
8719      /**
8720      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8721      */
8722     allowBlank : true,
8723     /**
8724      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8725      */
8726     blankText : "Please complete this mandatory field",
8727     
8728      /**
8729      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8730      */
8731     minLength : 0,
8732     /**
8733      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8734      */
8735     maxLength : Number.MAX_VALUE,
8736     /**
8737      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8738      */
8739     minLengthText : "The minimum length for this field is {0}",
8740     /**
8741      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8742      */
8743     maxLengthText : "The maximum length for this field is {0}",
8744   
8745     
8746     /**
8747      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8748      * If available, this function will be called only after the basic validators all return true, and will be passed the
8749      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8750      */
8751     validator : null,
8752     /**
8753      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8754      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8755      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8756      */
8757     regex : null,
8758     /**
8759      * @cfg {String} regexText -- Depricated - use Invalid Text
8760      */
8761     regexText : "",
8762     
8763     /**
8764      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8765      */
8766     invalidText : "",
8767     
8768     
8769     
8770     autocomplete: false,
8771     
8772     
8773     fieldLabel : '',
8774     inputType : 'text',
8775     
8776     name : false,
8777     placeholder: false,
8778     before : false,
8779     after : false,
8780     size : false,
8781     hasFocus : false,
8782     preventMark: false,
8783     isFormField : true,
8784     value : '',
8785     labelWidth : 2,
8786     labelAlign : false,
8787     readOnly : false,
8788     align : false,
8789     formatedValue : false,
8790     forceFeedback : false,
8791     
8792     indicatorpos : 'left',
8793     
8794     labellg : 0,
8795     labelmd : 0,
8796     labelsm : 0,
8797     labelxs : 0,
8798     
8799     capture : '',
8800     accept : '',
8801     
8802     parentLabelAlign : function()
8803     {
8804         var parent = this;
8805         while (parent.parent()) {
8806             parent = parent.parent();
8807             if (typeof(parent.labelAlign) !='undefined') {
8808                 return parent.labelAlign;
8809             }
8810         }
8811         return 'left';
8812         
8813     },
8814     
8815     getAutoCreate : function()
8816     {
8817         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8818         
8819         var id = Roo.id();
8820         
8821         var cfg = {};
8822         
8823         if(this.inputType != 'hidden'){
8824             cfg.cls = 'form-group' //input-group
8825         }
8826         
8827         var input =  {
8828             tag: 'input',
8829             id : id,
8830             type : this.inputType,
8831             value : this.value,
8832             cls : 'form-control',
8833             placeholder : this.placeholder || '',
8834             autocomplete : this.autocomplete || 'new-password'
8835         };
8836         
8837         if(this.capture.length){
8838             input.capture = this.capture;
8839         }
8840         
8841         if(this.accept.length){
8842             input.accept = this.accept + "/*";
8843         }
8844         
8845         if(this.align){
8846             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8847         }
8848         
8849         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8850             input.maxLength = this.maxLength;
8851         }
8852         
8853         if (this.disabled) {
8854             input.disabled=true;
8855         }
8856         
8857         if (this.readOnly) {
8858             input.readonly=true;
8859         }
8860         
8861         if (this.name) {
8862             input.name = this.name;
8863         }
8864         
8865         if (this.size) {
8866             input.cls += ' input-' + this.size;
8867         }
8868         
8869         var settings=this;
8870         ['xs','sm','md','lg'].map(function(size){
8871             if (settings[size]) {
8872                 cfg.cls += ' col-' + size + '-' + settings[size];
8873             }
8874         });
8875         
8876         var inputblock = input;
8877         
8878         var feedback = {
8879             tag: 'span',
8880             cls: 'glyphicon form-control-feedback'
8881         };
8882             
8883         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8884             
8885             inputblock = {
8886                 cls : 'has-feedback',
8887                 cn :  [
8888                     input,
8889                     feedback
8890                 ] 
8891             };  
8892         }
8893         
8894         if (this.before || this.after) {
8895             
8896             inputblock = {
8897                 cls : 'input-group',
8898                 cn :  [] 
8899             };
8900             
8901             if (this.before && typeof(this.before) == 'string') {
8902                 
8903                 inputblock.cn.push({
8904                     tag :'span',
8905                     cls : 'roo-input-before input-group-addon',
8906                     html : this.before
8907                 });
8908             }
8909             if (this.before && typeof(this.before) == 'object') {
8910                 this.before = Roo.factory(this.before);
8911                 
8912                 inputblock.cn.push({
8913                     tag :'span',
8914                     cls : 'roo-input-before input-group-' +
8915                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8916                 });
8917             }
8918             
8919             inputblock.cn.push(input);
8920             
8921             if (this.after && typeof(this.after) == 'string') {
8922                 inputblock.cn.push({
8923                     tag :'span',
8924                     cls : 'roo-input-after input-group-addon',
8925                     html : this.after
8926                 });
8927             }
8928             if (this.after && typeof(this.after) == 'object') {
8929                 this.after = Roo.factory(this.after);
8930                 
8931                 inputblock.cn.push({
8932                     tag :'span',
8933                     cls : 'roo-input-after input-group-' +
8934                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8935                 });
8936             }
8937             
8938             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8939                 inputblock.cls += ' has-feedback';
8940                 inputblock.cn.push(feedback);
8941             }
8942         };
8943         
8944         if (align ==='left' && this.fieldLabel.length) {
8945             
8946             cfg.cls += ' roo-form-group-label-left';
8947             
8948             cfg.cn = [
8949                 {
8950                     tag : 'i',
8951                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8952                     tooltip : 'This field is required'
8953                 },
8954                 {
8955                     tag: 'label',
8956                     'for' :  id,
8957                     cls : 'control-label',
8958                     html : this.fieldLabel
8959
8960                 },
8961                 {
8962                     cls : "", 
8963                     cn: [
8964                         inputblock
8965                     ]
8966                 }
8967             ];
8968             
8969             var labelCfg = cfg.cn[1];
8970             var contentCfg = cfg.cn[2];
8971             
8972             if(this.indicatorpos == 'right'){
8973                 cfg.cn = [
8974                     {
8975                         tag: 'label',
8976                         'for' :  id,
8977                         cls : 'control-label',
8978                         cn : [
8979                             {
8980                                 tag : 'span',
8981                                 html : this.fieldLabel
8982                             },
8983                             {
8984                                 tag : 'i',
8985                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8986                                 tooltip : 'This field is required'
8987                             }
8988                         ]
8989                     },
8990                     {
8991                         cls : "",
8992                         cn: [
8993                             inputblock
8994                         ]
8995                     }
8996
8997                 ];
8998                 
8999                 labelCfg = cfg.cn[0];
9000                 contentCfg = cfg.cn[1];
9001             
9002             }
9003             
9004             if(this.labelWidth > 12){
9005                 labelCfg.style = "width: " + this.labelWidth + 'px';
9006             }
9007             
9008             if(this.labelWidth < 13 && this.labelmd == 0){
9009                 this.labelmd = this.labelWidth;
9010             }
9011             
9012             if(this.labellg > 0){
9013                 labelCfg.cls += ' col-lg-' + this.labellg;
9014                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9015             }
9016             
9017             if(this.labelmd > 0){
9018                 labelCfg.cls += ' col-md-' + this.labelmd;
9019                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9020             }
9021             
9022             if(this.labelsm > 0){
9023                 labelCfg.cls += ' col-sm-' + this.labelsm;
9024                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9025             }
9026             
9027             if(this.labelxs > 0){
9028                 labelCfg.cls += ' col-xs-' + this.labelxs;
9029                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9030             }
9031             
9032             
9033         } else if ( this.fieldLabel.length) {
9034                 
9035             cfg.cn = [
9036                 {
9037                     tag : 'i',
9038                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9039                     tooltip : 'This field is required'
9040                 },
9041                 {
9042                     tag: 'label',
9043                    //cls : 'input-group-addon',
9044                     html : this.fieldLabel
9045
9046                 },
9047
9048                inputblock
9049
9050            ];
9051            
9052            if(this.indicatorpos == 'right'){
9053                 
9054                 cfg.cn = [
9055                     {
9056                         tag: 'label',
9057                        //cls : 'input-group-addon',
9058                         html : this.fieldLabel
9059
9060                     },
9061                     {
9062                         tag : 'i',
9063                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9064                         tooltip : 'This field is required'
9065                     },
9066
9067                    inputblock
9068
9069                ];
9070
9071             }
9072
9073         } else {
9074             
9075             cfg.cn = [
9076
9077                     inputblock
9078
9079             ];
9080                 
9081                 
9082         };
9083         
9084         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9085            cfg.cls += ' navbar-form';
9086         }
9087         
9088         if (this.parentType === 'NavGroup') {
9089            cfg.cls += ' navbar-form';
9090            cfg.tag = 'li';
9091         }
9092         
9093         return cfg;
9094         
9095     },
9096     /**
9097      * return the real input element.
9098      */
9099     inputEl: function ()
9100     {
9101         return this.el.select('input.form-control',true).first();
9102     },
9103     
9104     tooltipEl : function()
9105     {
9106         return this.inputEl();
9107     },
9108     
9109     indicatorEl : function()
9110     {
9111         var indicator = this.el.select('i.roo-required-indicator',true).first();
9112         
9113         if(!indicator){
9114             return false;
9115         }
9116         
9117         return indicator;
9118         
9119     },
9120     
9121     setDisabled : function(v)
9122     {
9123         var i  = this.inputEl().dom;
9124         if (!v) {
9125             i.removeAttribute('disabled');
9126             return;
9127             
9128         }
9129         i.setAttribute('disabled','true');
9130     },
9131     initEvents : function()
9132     {
9133           
9134         this.inputEl().on("keydown" , this.fireKey,  this);
9135         this.inputEl().on("focus", this.onFocus,  this);
9136         this.inputEl().on("blur", this.onBlur,  this);
9137         
9138         this.inputEl().relayEvent('keyup', this);
9139         
9140         this.indicator = this.indicatorEl();
9141         
9142         if(this.indicator){
9143             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9144         }
9145  
9146         // reference to original value for reset
9147         this.originalValue = this.getValue();
9148         //Roo.form.TextField.superclass.initEvents.call(this);
9149         if(this.validationEvent == 'keyup'){
9150             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9151             this.inputEl().on('keyup', this.filterValidation, this);
9152         }
9153         else if(this.validationEvent !== false){
9154             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9155         }
9156         
9157         if(this.selectOnFocus){
9158             this.on("focus", this.preFocus, this);
9159             
9160         }
9161         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9162             this.inputEl().on("keypress", this.filterKeys, this);
9163         } else {
9164             this.inputEl().relayEvent('keypress', this);
9165         }
9166        /* if(this.grow){
9167             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9168             this.el.on("click", this.autoSize,  this);
9169         }
9170         */
9171         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9172             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9173         }
9174         
9175         if (typeof(this.before) == 'object') {
9176             this.before.render(this.el.select('.roo-input-before',true).first());
9177         }
9178         if (typeof(this.after) == 'object') {
9179             this.after.render(this.el.select('.roo-input-after',true).first());
9180         }
9181         
9182         this.inputEl().on('change', this.onChange, this);
9183         
9184     },
9185     filterValidation : function(e){
9186         if(!e.isNavKeyPress()){
9187             this.validationTask.delay(this.validationDelay);
9188         }
9189     },
9190      /**
9191      * Validates the field value
9192      * @return {Boolean} True if the value is valid, else false
9193      */
9194     validate : function(){
9195         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9196         if(this.disabled || this.validateValue(this.getRawValue())){
9197             this.markValid();
9198             return true;
9199         }
9200         
9201         this.markInvalid();
9202         return false;
9203     },
9204     
9205     
9206     /**
9207      * Validates a value according to the field's validation rules and marks the field as invalid
9208      * if the validation fails
9209      * @param {Mixed} value The value to validate
9210      * @return {Boolean} True if the value is valid, else false
9211      */
9212     validateValue : function(value)
9213     {
9214         if(this.getVisibilityEl().hasClass('hidden')){
9215             return true;
9216         }
9217         
9218         if(value.length < 1)  { // if it's blank
9219             if(this.allowBlank){
9220                 return true;
9221             }
9222             return false;
9223         }
9224         
9225         if(value.length < this.minLength){
9226             return false;
9227         }
9228         if(value.length > this.maxLength){
9229             return false;
9230         }
9231         if(this.vtype){
9232             var vt = Roo.form.VTypes;
9233             if(!vt[this.vtype](value, this)){
9234                 return false;
9235             }
9236         }
9237         if(typeof this.validator == "function"){
9238             var msg = this.validator(value);
9239             if(msg !== true){
9240                 return false;
9241             }
9242             if (typeof(msg) == 'string') {
9243                 this.invalidText = msg;
9244             }
9245         }
9246         
9247         if(this.regex && !this.regex.test(value)){
9248             return false;
9249         }
9250         
9251         return true;
9252     },
9253     
9254      // private
9255     fireKey : function(e){
9256         //Roo.log('field ' + e.getKey());
9257         if(e.isNavKeyPress()){
9258             this.fireEvent("specialkey", this, e);
9259         }
9260     },
9261     focus : function (selectText){
9262         if(this.rendered){
9263             this.inputEl().focus();
9264             if(selectText === true){
9265                 this.inputEl().dom.select();
9266             }
9267         }
9268         return this;
9269     } ,
9270     
9271     onFocus : function(){
9272         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9273            // this.el.addClass(this.focusClass);
9274         }
9275         if(!this.hasFocus){
9276             this.hasFocus = true;
9277             this.startValue = this.getValue();
9278             this.fireEvent("focus", this);
9279         }
9280     },
9281     
9282     beforeBlur : Roo.emptyFn,
9283
9284     
9285     // private
9286     onBlur : function(){
9287         this.beforeBlur();
9288         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9289             //this.el.removeClass(this.focusClass);
9290         }
9291         this.hasFocus = false;
9292         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9293             this.validate();
9294         }
9295         var v = this.getValue();
9296         if(String(v) !== String(this.startValue)){
9297             this.fireEvent('change', this, v, this.startValue);
9298         }
9299         this.fireEvent("blur", this);
9300     },
9301     
9302     onChange : function(e)
9303     {
9304         var v = this.getValue();
9305         if(String(v) !== String(this.startValue)){
9306             this.fireEvent('change', this, v, this.startValue);
9307         }
9308         
9309     },
9310     
9311     /**
9312      * Resets the current field value to the originally loaded value and clears any validation messages
9313      */
9314     reset : function(){
9315         this.setValue(this.originalValue);
9316         this.validate();
9317     },
9318      /**
9319      * Returns the name of the field
9320      * @return {Mixed} name The name field
9321      */
9322     getName: function(){
9323         return this.name;
9324     },
9325      /**
9326      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9327      * @return {Mixed} value The field value
9328      */
9329     getValue : function(){
9330         
9331         var v = this.inputEl().getValue();
9332         
9333         return v;
9334     },
9335     /**
9336      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9337      * @return {Mixed} value The field value
9338      */
9339     getRawValue : function(){
9340         var v = this.inputEl().getValue();
9341         
9342         return v;
9343     },
9344     
9345     /**
9346      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9347      * @param {Mixed} value The value to set
9348      */
9349     setRawValue : function(v){
9350         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9351     },
9352     
9353     selectText : function(start, end){
9354         var v = this.getRawValue();
9355         if(v.length > 0){
9356             start = start === undefined ? 0 : start;
9357             end = end === undefined ? v.length : end;
9358             var d = this.inputEl().dom;
9359             if(d.setSelectionRange){
9360                 d.setSelectionRange(start, end);
9361             }else if(d.createTextRange){
9362                 var range = d.createTextRange();
9363                 range.moveStart("character", start);
9364                 range.moveEnd("character", v.length-end);
9365                 range.select();
9366             }
9367         }
9368     },
9369     
9370     /**
9371      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9372      * @param {Mixed} value The value to set
9373      */
9374     setValue : function(v){
9375         this.value = v;
9376         if(this.rendered){
9377             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9378             this.validate();
9379         }
9380     },
9381     
9382     /*
9383     processValue : function(value){
9384         if(this.stripCharsRe){
9385             var newValue = value.replace(this.stripCharsRe, '');
9386             if(newValue !== value){
9387                 this.setRawValue(newValue);
9388                 return newValue;
9389             }
9390         }
9391         return value;
9392     },
9393   */
9394     preFocus : function(){
9395         
9396         if(this.selectOnFocus){
9397             this.inputEl().dom.select();
9398         }
9399     },
9400     filterKeys : function(e){
9401         var k = e.getKey();
9402         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9403             return;
9404         }
9405         var c = e.getCharCode(), cc = String.fromCharCode(c);
9406         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9407             return;
9408         }
9409         if(!this.maskRe.test(cc)){
9410             e.stopEvent();
9411         }
9412     },
9413      /**
9414      * Clear any invalid styles/messages for this field
9415      */
9416     clearInvalid : function(){
9417         
9418         if(!this.el || this.preventMark){ // not rendered
9419             return;
9420         }
9421         
9422      
9423         this.el.removeClass(this.invalidClass);
9424         
9425         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9426             
9427             var feedback = this.el.select('.form-control-feedback', true).first();
9428             
9429             if(feedback){
9430                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9431             }
9432             
9433         }
9434         
9435         if(this.indicator){
9436             this.indicator.removeClass('visible');
9437             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9438         }
9439         
9440         this.fireEvent('valid', this);
9441     },
9442     
9443      /**
9444      * Mark this field as valid
9445      */
9446     markValid : function()
9447     {
9448         if(!this.el  || this.preventMark){ // not rendered...
9449             return;
9450         }
9451         
9452         this.el.removeClass([this.invalidClass, this.validClass]);
9453         
9454         var feedback = this.el.select('.form-control-feedback', true).first();
9455             
9456         if(feedback){
9457             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9458         }
9459         
9460         if(this.indicator){
9461             this.indicator.removeClass('visible');
9462             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9463         }
9464         
9465         if(this.disabled){
9466             return;
9467         }
9468         
9469         if(this.allowBlank && !this.getRawValue().length){
9470             return;
9471         }
9472         
9473         this.el.addClass(this.validClass);
9474         
9475         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9476             
9477             var feedback = this.el.select('.form-control-feedback', true).first();
9478             
9479             if(feedback){
9480                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9481                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9482             }
9483             
9484         }
9485         
9486         this.fireEvent('valid', this);
9487     },
9488     
9489      /**
9490      * Mark this field as invalid
9491      * @param {String} msg The validation message
9492      */
9493     markInvalid : function(msg)
9494     {
9495         if(!this.el  || this.preventMark){ // not rendered
9496             return;
9497         }
9498         
9499         this.el.removeClass([this.invalidClass, this.validClass]);
9500         
9501         var feedback = this.el.select('.form-control-feedback', true).first();
9502             
9503         if(feedback){
9504             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9505         }
9506
9507         if(this.disabled){
9508             return;
9509         }
9510         
9511         if(this.allowBlank && !this.getRawValue().length){
9512             return;
9513         }
9514         
9515         if(this.indicator){
9516             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9517             this.indicator.addClass('visible');
9518         }
9519         
9520         this.el.addClass(this.invalidClass);
9521         
9522         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9523             
9524             var feedback = this.el.select('.form-control-feedback', true).first();
9525             
9526             if(feedback){
9527                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9528                 
9529                 if(this.getValue().length || this.forceFeedback){
9530                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9531                 }
9532                 
9533             }
9534             
9535         }
9536         
9537         this.fireEvent('invalid', this, msg);
9538     },
9539     // private
9540     SafariOnKeyDown : function(event)
9541     {
9542         // this is a workaround for a password hang bug on chrome/ webkit.
9543         if (this.inputEl().dom.type != 'password') {
9544             return;
9545         }
9546         
9547         var isSelectAll = false;
9548         
9549         if(this.inputEl().dom.selectionEnd > 0){
9550             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9551         }
9552         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9553             event.preventDefault();
9554             this.setValue('');
9555             return;
9556         }
9557         
9558         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9559             
9560             event.preventDefault();
9561             // this is very hacky as keydown always get's upper case.
9562             //
9563             var cc = String.fromCharCode(event.getCharCode());
9564             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9565             
9566         }
9567     },
9568     adjustWidth : function(tag, w){
9569         tag = tag.toLowerCase();
9570         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9571             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9572                 if(tag == 'input'){
9573                     return w + 2;
9574                 }
9575                 if(tag == 'textarea'){
9576                     return w-2;
9577                 }
9578             }else if(Roo.isOpera){
9579                 if(tag == 'input'){
9580                     return w + 2;
9581                 }
9582                 if(tag == 'textarea'){
9583                     return w-2;
9584                 }
9585             }
9586         }
9587         return w;
9588     },
9589     
9590     setFieldLabel : function(v)
9591     {
9592         if(!this.rendered){
9593             return;
9594         }
9595         
9596         if(this.indicator){
9597             var ar = this.el.select('label > span',true);
9598             
9599             if (ar.elements.length) {
9600                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9601                 this.fieldLabel = v;
9602                 return;
9603             }
9604             
9605             var br = this.el.select('label',true);
9606             
9607             if(br.elements.length) {
9608                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9609                 this.fieldLabel = v;
9610                 return;
9611             }
9612             
9613             Roo.log('Cannot Found any of label > span || label in input');
9614             return;
9615         }
9616         
9617         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9618         this.fieldLabel = v;
9619         
9620         
9621     }
9622 });
9623
9624  
9625 /*
9626  * - LGPL
9627  *
9628  * Input
9629  * 
9630  */
9631
9632 /**
9633  * @class Roo.bootstrap.TextArea
9634  * @extends Roo.bootstrap.Input
9635  * Bootstrap TextArea class
9636  * @cfg {Number} cols Specifies the visible width of a text area
9637  * @cfg {Number} rows Specifies the visible number of lines in a text area
9638  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9639  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9640  * @cfg {string} html text
9641  * 
9642  * @constructor
9643  * Create a new TextArea
9644  * @param {Object} config The config object
9645  */
9646
9647 Roo.bootstrap.TextArea = function(config){
9648     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9649    
9650 };
9651
9652 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9653      
9654     cols : false,
9655     rows : 5,
9656     readOnly : false,
9657     warp : 'soft',
9658     resize : false,
9659     value: false,
9660     html: false,
9661     
9662     getAutoCreate : function(){
9663         
9664         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9665         
9666         var id = Roo.id();
9667         
9668         var cfg = {};
9669         
9670         if(this.inputType != 'hidden'){
9671             cfg.cls = 'form-group' //input-group
9672         }
9673         
9674         var input =  {
9675             tag: 'textarea',
9676             id : id,
9677             warp : this.warp,
9678             rows : this.rows,
9679             value : this.value || '',
9680             html: this.html || '',
9681             cls : 'form-control',
9682             placeholder : this.placeholder || '' 
9683             
9684         };
9685         
9686         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9687             input.maxLength = this.maxLength;
9688         }
9689         
9690         if(this.resize){
9691             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9692         }
9693         
9694         if(this.cols){
9695             input.cols = this.cols;
9696         }
9697         
9698         if (this.readOnly) {
9699             input.readonly = true;
9700         }
9701         
9702         if (this.name) {
9703             input.name = this.name;
9704         }
9705         
9706         if (this.size) {
9707             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9708         }
9709         
9710         var settings=this;
9711         ['xs','sm','md','lg'].map(function(size){
9712             if (settings[size]) {
9713                 cfg.cls += ' col-' + size + '-' + settings[size];
9714             }
9715         });
9716         
9717         var inputblock = input;
9718         
9719         if(this.hasFeedback && !this.allowBlank){
9720             
9721             var feedback = {
9722                 tag: 'span',
9723                 cls: 'glyphicon form-control-feedback'
9724             };
9725
9726             inputblock = {
9727                 cls : 'has-feedback',
9728                 cn :  [
9729                     input,
9730                     feedback
9731                 ] 
9732             };  
9733         }
9734         
9735         
9736         if (this.before || this.after) {
9737             
9738             inputblock = {
9739                 cls : 'input-group',
9740                 cn :  [] 
9741             };
9742             if (this.before) {
9743                 inputblock.cn.push({
9744                     tag :'span',
9745                     cls : 'input-group-addon',
9746                     html : this.before
9747                 });
9748             }
9749             
9750             inputblock.cn.push(input);
9751             
9752             if(this.hasFeedback && !this.allowBlank){
9753                 inputblock.cls += ' has-feedback';
9754                 inputblock.cn.push(feedback);
9755             }
9756             
9757             if (this.after) {
9758                 inputblock.cn.push({
9759                     tag :'span',
9760                     cls : 'input-group-addon',
9761                     html : this.after
9762                 });
9763             }
9764             
9765         }
9766         
9767         if (align ==='left' && this.fieldLabel.length) {
9768             cfg.cn = [
9769                 {
9770                     tag: 'label',
9771                     'for' :  id,
9772                     cls : 'control-label',
9773                     html : this.fieldLabel
9774                 },
9775                 {
9776                     cls : "",
9777                     cn: [
9778                         inputblock
9779                     ]
9780                 }
9781
9782             ];
9783             
9784             if(this.labelWidth > 12){
9785                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9786             }
9787
9788             if(this.labelWidth < 13 && this.labelmd == 0){
9789                 this.labelmd = this.labelWidth;
9790             }
9791
9792             if(this.labellg > 0){
9793                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9794                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9795             }
9796
9797             if(this.labelmd > 0){
9798                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9799                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9800             }
9801
9802             if(this.labelsm > 0){
9803                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9804                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9805             }
9806
9807             if(this.labelxs > 0){
9808                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9809                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9810             }
9811             
9812         } else if ( this.fieldLabel.length) {
9813             cfg.cn = [
9814
9815                {
9816                    tag: 'label',
9817                    //cls : 'input-group-addon',
9818                    html : this.fieldLabel
9819
9820                },
9821
9822                inputblock
9823
9824            ];
9825
9826         } else {
9827
9828             cfg.cn = [
9829
9830                 inputblock
9831
9832             ];
9833                 
9834         }
9835         
9836         if (this.disabled) {
9837             input.disabled=true;
9838         }
9839         
9840         return cfg;
9841         
9842     },
9843     /**
9844      * return the real textarea element.
9845      */
9846     inputEl: function ()
9847     {
9848         return this.el.select('textarea.form-control',true).first();
9849     },
9850     
9851     /**
9852      * Clear any invalid styles/messages for this field
9853      */
9854     clearInvalid : function()
9855     {
9856         
9857         if(!this.el || this.preventMark){ // not rendered
9858             return;
9859         }
9860         
9861         var label = this.el.select('label', true).first();
9862         var icon = this.el.select('i.fa-star', true).first();
9863         
9864         if(label && icon){
9865             icon.remove();
9866         }
9867         
9868         this.el.removeClass(this.invalidClass);
9869         
9870         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9871             
9872             var feedback = this.el.select('.form-control-feedback', true).first();
9873             
9874             if(feedback){
9875                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9876             }
9877             
9878         }
9879         
9880         this.fireEvent('valid', this);
9881     },
9882     
9883      /**
9884      * Mark this field as valid
9885      */
9886     markValid : function()
9887     {
9888         if(!this.el  || this.preventMark){ // not rendered
9889             return;
9890         }
9891         
9892         this.el.removeClass([this.invalidClass, this.validClass]);
9893         
9894         var feedback = this.el.select('.form-control-feedback', true).first();
9895             
9896         if(feedback){
9897             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9898         }
9899
9900         if(this.disabled || this.allowBlank){
9901             return;
9902         }
9903         
9904         var label = this.el.select('label', true).first();
9905         var icon = this.el.select('i.fa-star', true).first();
9906         
9907         if(label && icon){
9908             icon.remove();
9909         }
9910         
9911         this.el.addClass(this.validClass);
9912         
9913         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9914             
9915             var feedback = this.el.select('.form-control-feedback', true).first();
9916             
9917             if(feedback){
9918                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9919                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9920             }
9921             
9922         }
9923         
9924         this.fireEvent('valid', this);
9925     },
9926     
9927      /**
9928      * Mark this field as invalid
9929      * @param {String} msg The validation message
9930      */
9931     markInvalid : function(msg)
9932     {
9933         if(!this.el  || this.preventMark){ // not rendered
9934             return;
9935         }
9936         
9937         this.el.removeClass([this.invalidClass, this.validClass]);
9938         
9939         var feedback = this.el.select('.form-control-feedback', true).first();
9940             
9941         if(feedback){
9942             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9943         }
9944
9945         if(this.disabled || this.allowBlank){
9946             return;
9947         }
9948         
9949         var label = this.el.select('label', true).first();
9950         var icon = this.el.select('i.fa-star', true).first();
9951         
9952         if(!this.getValue().length && label && !icon){
9953             this.el.createChild({
9954                 tag : 'i',
9955                 cls : 'text-danger fa fa-lg fa-star',
9956                 tooltip : 'This field is required',
9957                 style : 'margin-right:5px;'
9958             }, label, true);
9959         }
9960
9961         this.el.addClass(this.invalidClass);
9962         
9963         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9964             
9965             var feedback = this.el.select('.form-control-feedback', true).first();
9966             
9967             if(feedback){
9968                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9969                 
9970                 if(this.getValue().length || this.forceFeedback){
9971                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9972                 }
9973                 
9974             }
9975             
9976         }
9977         
9978         this.fireEvent('invalid', this, msg);
9979     }
9980 });
9981
9982  
9983 /*
9984  * - LGPL
9985  *
9986  * trigger field - base class for combo..
9987  * 
9988  */
9989  
9990 /**
9991  * @class Roo.bootstrap.TriggerField
9992  * @extends Roo.bootstrap.Input
9993  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9994  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9995  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9996  * for which you can provide a custom implementation.  For example:
9997  * <pre><code>
9998 var trigger = new Roo.bootstrap.TriggerField();
9999 trigger.onTriggerClick = myTriggerFn;
10000 trigger.applyTo('my-field');
10001 </code></pre>
10002  *
10003  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10004  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10005  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10006  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10007  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10008
10009  * @constructor
10010  * Create a new TriggerField.
10011  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10012  * to the base TextField)
10013  */
10014 Roo.bootstrap.TriggerField = function(config){
10015     this.mimicing = false;
10016     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10017 };
10018
10019 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10020     /**
10021      * @cfg {String} triggerClass A CSS class to apply to the trigger
10022      */
10023      /**
10024      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10025      */
10026     hideTrigger:false,
10027
10028     /**
10029      * @cfg {Boolean} removable (true|false) special filter default false
10030      */
10031     removable : false,
10032     
10033     /** @cfg {Boolean} grow @hide */
10034     /** @cfg {Number} growMin @hide */
10035     /** @cfg {Number} growMax @hide */
10036
10037     /**
10038      * @hide 
10039      * @method
10040      */
10041     autoSize: Roo.emptyFn,
10042     // private
10043     monitorTab : true,
10044     // private
10045     deferHeight : true,
10046
10047     
10048     actionMode : 'wrap',
10049     
10050     caret : false,
10051     
10052     
10053     getAutoCreate : function(){
10054        
10055         var align = this.labelAlign || this.parentLabelAlign();
10056         
10057         var id = Roo.id();
10058         
10059         var cfg = {
10060             cls: 'form-group' //input-group
10061         };
10062         
10063         
10064         var input =  {
10065             tag: 'input',
10066             id : id,
10067             type : this.inputType,
10068             cls : 'form-control',
10069             autocomplete: 'new-password',
10070             placeholder : this.placeholder || '' 
10071             
10072         };
10073         if (this.name) {
10074             input.name = this.name;
10075         }
10076         if (this.size) {
10077             input.cls += ' input-' + this.size;
10078         }
10079         
10080         if (this.disabled) {
10081             input.disabled=true;
10082         }
10083         
10084         var inputblock = input;
10085         
10086         if(this.hasFeedback && !this.allowBlank){
10087             
10088             var feedback = {
10089                 tag: 'span',
10090                 cls: 'glyphicon form-control-feedback'
10091             };
10092             
10093             if(this.removable && !this.editable && !this.tickable){
10094                 inputblock = {
10095                     cls : 'has-feedback',
10096                     cn :  [
10097                         inputblock,
10098                         {
10099                             tag: 'button',
10100                             html : 'x',
10101                             cls : 'roo-combo-removable-btn close'
10102                         },
10103                         feedback
10104                     ] 
10105                 };
10106             } else {
10107                 inputblock = {
10108                     cls : 'has-feedback',
10109                     cn :  [
10110                         inputblock,
10111                         feedback
10112                     ] 
10113                 };
10114             }
10115
10116         } else {
10117             if(this.removable && !this.editable && !this.tickable){
10118                 inputblock = {
10119                     cls : 'roo-removable',
10120                     cn :  [
10121                         inputblock,
10122                         {
10123                             tag: 'button',
10124                             html : 'x',
10125                             cls : 'roo-combo-removable-btn close'
10126                         }
10127                     ] 
10128                 };
10129             }
10130         }
10131         
10132         if (this.before || this.after) {
10133             
10134             inputblock = {
10135                 cls : 'input-group',
10136                 cn :  [] 
10137             };
10138             if (this.before) {
10139                 inputblock.cn.push({
10140                     tag :'span',
10141                     cls : 'input-group-addon',
10142                     html : this.before
10143                 });
10144             }
10145             
10146             inputblock.cn.push(input);
10147             
10148             if(this.hasFeedback && !this.allowBlank){
10149                 inputblock.cls += ' has-feedback';
10150                 inputblock.cn.push(feedback);
10151             }
10152             
10153             if (this.after) {
10154                 inputblock.cn.push({
10155                     tag :'span',
10156                     cls : 'input-group-addon',
10157                     html : this.after
10158                 });
10159             }
10160             
10161         };
10162         
10163         var box = {
10164             tag: 'div',
10165             cn: [
10166                 {
10167                     tag: 'input',
10168                     type : 'hidden',
10169                     cls: 'form-hidden-field'
10170                 },
10171                 inputblock
10172             ]
10173             
10174         };
10175         
10176         if(this.multiple){
10177             box = {
10178                 tag: 'div',
10179                 cn: [
10180                     {
10181                         tag: 'input',
10182                         type : 'hidden',
10183                         cls: 'form-hidden-field'
10184                     },
10185                     {
10186                         tag: 'ul',
10187                         cls: 'roo-select2-choices',
10188                         cn:[
10189                             {
10190                                 tag: 'li',
10191                                 cls: 'roo-select2-search-field',
10192                                 cn: [
10193
10194                                     inputblock
10195                                 ]
10196                             }
10197                         ]
10198                     }
10199                 ]
10200             }
10201         };
10202         
10203         var combobox = {
10204             cls: 'roo-select2-container input-group',
10205             cn: [
10206                 box
10207 //                {
10208 //                    tag: 'ul',
10209 //                    cls: 'typeahead typeahead-long dropdown-menu',
10210 //                    style: 'display:none'
10211 //                }
10212             ]
10213         };
10214         
10215         if(!this.multiple && this.showToggleBtn){
10216             
10217             var caret = {
10218                         tag: 'span',
10219                         cls: 'caret'
10220              };
10221             if (this.caret != false) {
10222                 caret = {
10223                      tag: 'i',
10224                      cls: 'fa fa-' + this.caret
10225                 };
10226                 
10227             }
10228             
10229             combobox.cn.push({
10230                 tag :'span',
10231                 cls : 'input-group-addon btn dropdown-toggle',
10232                 cn : [
10233                     caret,
10234                     {
10235                         tag: 'span',
10236                         cls: 'combobox-clear',
10237                         cn  : [
10238                             {
10239                                 tag : 'i',
10240                                 cls: 'icon-remove'
10241                             }
10242                         ]
10243                     }
10244                 ]
10245
10246             })
10247         }
10248         
10249         if(this.multiple){
10250             combobox.cls += ' roo-select2-container-multi';
10251         }
10252         
10253         if (align ==='left' && this.fieldLabel.length) {
10254             
10255             cfg.cls += ' roo-form-group-label-left';
10256
10257             cfg.cn = [
10258                 {
10259                     tag : 'i',
10260                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10261                     tooltip : 'This field is required'
10262                 },
10263                 {
10264                     tag: 'label',
10265                     'for' :  id,
10266                     cls : 'control-label',
10267                     html : this.fieldLabel
10268
10269                 },
10270                 {
10271                     cls : "", 
10272                     cn: [
10273                         combobox
10274                     ]
10275                 }
10276
10277             ];
10278             
10279             var labelCfg = cfg.cn[1];
10280             var contentCfg = cfg.cn[2];
10281             
10282             if(this.indicatorpos == 'right'){
10283                 cfg.cn = [
10284                     {
10285                         tag: 'label',
10286                         'for' :  id,
10287                         cls : 'control-label',
10288                         cn : [
10289                             {
10290                                 tag : 'span',
10291                                 html : this.fieldLabel
10292                             },
10293                             {
10294                                 tag : 'i',
10295                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10296                                 tooltip : 'This field is required'
10297                             }
10298                         ]
10299                     },
10300                     {
10301                         cls : "", 
10302                         cn: [
10303                             combobox
10304                         ]
10305                     }
10306
10307                 ];
10308                 
10309                 labelCfg = cfg.cn[0];
10310                 contentCfg = cfg.cn[1];
10311             }
10312             
10313             if(this.labelWidth > 12){
10314                 labelCfg.style = "width: " + this.labelWidth + 'px';
10315             }
10316             
10317             if(this.labelWidth < 13 && this.labelmd == 0){
10318                 this.labelmd = this.labelWidth;
10319             }
10320             
10321             if(this.labellg > 0){
10322                 labelCfg.cls += ' col-lg-' + this.labellg;
10323                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10324             }
10325             
10326             if(this.labelmd > 0){
10327                 labelCfg.cls += ' col-md-' + this.labelmd;
10328                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10329             }
10330             
10331             if(this.labelsm > 0){
10332                 labelCfg.cls += ' col-sm-' + this.labelsm;
10333                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10334             }
10335             
10336             if(this.labelxs > 0){
10337                 labelCfg.cls += ' col-xs-' + this.labelxs;
10338                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10339             }
10340             
10341         } else if ( this.fieldLabel.length) {
10342 //                Roo.log(" label");
10343             cfg.cn = [
10344                 {
10345                    tag : 'i',
10346                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10347                    tooltip : 'This field is required'
10348                },
10349                {
10350                    tag: 'label',
10351                    //cls : 'input-group-addon',
10352                    html : this.fieldLabel
10353
10354                },
10355
10356                combobox
10357
10358             ];
10359             
10360             if(this.indicatorpos == 'right'){
10361                 
10362                 cfg.cn = [
10363                     {
10364                        tag: 'label',
10365                        cn : [
10366                            {
10367                                tag : 'span',
10368                                html : this.fieldLabel
10369                            },
10370                            {
10371                               tag : 'i',
10372                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10373                               tooltip : 'This field is required'
10374                            }
10375                        ]
10376
10377                     },
10378                     combobox
10379
10380                 ];
10381
10382             }
10383
10384         } else {
10385             
10386 //                Roo.log(" no label && no align");
10387                 cfg = combobox
10388                      
10389                 
10390         }
10391         
10392         var settings=this;
10393         ['xs','sm','md','lg'].map(function(size){
10394             if (settings[size]) {
10395                 cfg.cls += ' col-' + size + '-' + settings[size];
10396             }
10397         });
10398         
10399         return cfg;
10400         
10401     },
10402     
10403     
10404     
10405     // private
10406     onResize : function(w, h){
10407 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10408 //        if(typeof w == 'number'){
10409 //            var x = w - this.trigger.getWidth();
10410 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10411 //            this.trigger.setStyle('left', x+'px');
10412 //        }
10413     },
10414
10415     // private
10416     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10417
10418     // private
10419     getResizeEl : function(){
10420         return this.inputEl();
10421     },
10422
10423     // private
10424     getPositionEl : function(){
10425         return this.inputEl();
10426     },
10427
10428     // private
10429     alignErrorIcon : function(){
10430         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10431     },
10432
10433     // private
10434     initEvents : function(){
10435         
10436         this.createList();
10437         
10438         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10439         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10440         if(!this.multiple && this.showToggleBtn){
10441             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10442             if(this.hideTrigger){
10443                 this.trigger.setDisplayed(false);
10444             }
10445             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10446         }
10447         
10448         if(this.multiple){
10449             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10450         }
10451         
10452         if(this.removable && !this.editable && !this.tickable){
10453             var close = this.closeTriggerEl();
10454             
10455             if(close){
10456                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10457                 close.on('click', this.removeBtnClick, this, close);
10458             }
10459         }
10460         
10461         //this.trigger.addClassOnOver('x-form-trigger-over');
10462         //this.trigger.addClassOnClick('x-form-trigger-click');
10463         
10464         //if(!this.width){
10465         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10466         //}
10467     },
10468     
10469     closeTriggerEl : function()
10470     {
10471         var close = this.el.select('.roo-combo-removable-btn', true).first();
10472         return close ? close : false;
10473     },
10474     
10475     removeBtnClick : function(e, h, el)
10476     {
10477         e.preventDefault();
10478         
10479         if(this.fireEvent("remove", this) !== false){
10480             this.reset();
10481             this.fireEvent("afterremove", this)
10482         }
10483     },
10484     
10485     createList : function()
10486     {
10487         this.list = Roo.get(document.body).createChild({
10488             tag: 'ul',
10489             cls: 'typeahead typeahead-long dropdown-menu',
10490             style: 'display:none'
10491         });
10492         
10493         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10494         
10495     },
10496
10497     // private
10498     initTrigger : function(){
10499        
10500     },
10501
10502     // private
10503     onDestroy : function(){
10504         if(this.trigger){
10505             this.trigger.removeAllListeners();
10506           //  this.trigger.remove();
10507         }
10508         //if(this.wrap){
10509         //    this.wrap.remove();
10510         //}
10511         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10512     },
10513
10514     // private
10515     onFocus : function(){
10516         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10517         /*
10518         if(!this.mimicing){
10519             this.wrap.addClass('x-trigger-wrap-focus');
10520             this.mimicing = true;
10521             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10522             if(this.monitorTab){
10523                 this.el.on("keydown", this.checkTab, this);
10524             }
10525         }
10526         */
10527     },
10528
10529     // private
10530     checkTab : function(e){
10531         if(e.getKey() == e.TAB){
10532             this.triggerBlur();
10533         }
10534     },
10535
10536     // private
10537     onBlur : function(){
10538         // do nothing
10539     },
10540
10541     // private
10542     mimicBlur : function(e, t){
10543         /*
10544         if(!this.wrap.contains(t) && this.validateBlur()){
10545             this.triggerBlur();
10546         }
10547         */
10548     },
10549
10550     // private
10551     triggerBlur : function(){
10552         this.mimicing = false;
10553         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10554         if(this.monitorTab){
10555             this.el.un("keydown", this.checkTab, this);
10556         }
10557         //this.wrap.removeClass('x-trigger-wrap-focus');
10558         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10559     },
10560
10561     // private
10562     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10563     validateBlur : function(e, t){
10564         return true;
10565     },
10566
10567     // private
10568     onDisable : function(){
10569         this.inputEl().dom.disabled = true;
10570         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10571         //if(this.wrap){
10572         //    this.wrap.addClass('x-item-disabled');
10573         //}
10574     },
10575
10576     // private
10577     onEnable : function(){
10578         this.inputEl().dom.disabled = false;
10579         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10580         //if(this.wrap){
10581         //    this.el.removeClass('x-item-disabled');
10582         //}
10583     },
10584
10585     // private
10586     onShow : function(){
10587         var ae = this.getActionEl();
10588         
10589         if(ae){
10590             ae.dom.style.display = '';
10591             ae.dom.style.visibility = 'visible';
10592         }
10593     },
10594
10595     // private
10596     
10597     onHide : function(){
10598         var ae = this.getActionEl();
10599         ae.dom.style.display = 'none';
10600     },
10601
10602     /**
10603      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10604      * by an implementing function.
10605      * @method
10606      * @param {EventObject} e
10607      */
10608     onTriggerClick : Roo.emptyFn
10609 });
10610  /*
10611  * Based on:
10612  * Ext JS Library 1.1.1
10613  * Copyright(c) 2006-2007, Ext JS, LLC.
10614  *
10615  * Originally Released Under LGPL - original licence link has changed is not relivant.
10616  *
10617  * Fork - LGPL
10618  * <script type="text/javascript">
10619  */
10620
10621
10622 /**
10623  * @class Roo.data.SortTypes
10624  * @singleton
10625  * Defines the default sorting (casting?) comparison functions used when sorting data.
10626  */
10627 Roo.data.SortTypes = {
10628     /**
10629      * Default sort that does nothing
10630      * @param {Mixed} s The value being converted
10631      * @return {Mixed} The comparison value
10632      */
10633     none : function(s){
10634         return s;
10635     },
10636     
10637     /**
10638      * The regular expression used to strip tags
10639      * @type {RegExp}
10640      * @property
10641      */
10642     stripTagsRE : /<\/?[^>]+>/gi,
10643     
10644     /**
10645      * Strips all HTML tags to sort on text only
10646      * @param {Mixed} s The value being converted
10647      * @return {String} The comparison value
10648      */
10649     asText : function(s){
10650         return String(s).replace(this.stripTagsRE, "");
10651     },
10652     
10653     /**
10654      * Strips all HTML tags to sort on text only - Case insensitive
10655      * @param {Mixed} s The value being converted
10656      * @return {String} The comparison value
10657      */
10658     asUCText : function(s){
10659         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10660     },
10661     
10662     /**
10663      * Case insensitive string
10664      * @param {Mixed} s The value being converted
10665      * @return {String} The comparison value
10666      */
10667     asUCString : function(s) {
10668         return String(s).toUpperCase();
10669     },
10670     
10671     /**
10672      * Date sorting
10673      * @param {Mixed} s The value being converted
10674      * @return {Number} The comparison value
10675      */
10676     asDate : function(s) {
10677         if(!s){
10678             return 0;
10679         }
10680         if(s instanceof Date){
10681             return s.getTime();
10682         }
10683         return Date.parse(String(s));
10684     },
10685     
10686     /**
10687      * Float sorting
10688      * @param {Mixed} s The value being converted
10689      * @return {Float} The comparison value
10690      */
10691     asFloat : function(s) {
10692         var val = parseFloat(String(s).replace(/,/g, ""));
10693         if(isNaN(val)) {
10694             val = 0;
10695         }
10696         return val;
10697     },
10698     
10699     /**
10700      * Integer sorting
10701      * @param {Mixed} s The value being converted
10702      * @return {Number} The comparison value
10703      */
10704     asInt : function(s) {
10705         var val = parseInt(String(s).replace(/,/g, ""));
10706         if(isNaN(val)) {
10707             val = 0;
10708         }
10709         return val;
10710     }
10711 };/*
10712  * Based on:
10713  * Ext JS Library 1.1.1
10714  * Copyright(c) 2006-2007, Ext JS, LLC.
10715  *
10716  * Originally Released Under LGPL - original licence link has changed is not relivant.
10717  *
10718  * Fork - LGPL
10719  * <script type="text/javascript">
10720  */
10721
10722 /**
10723 * @class Roo.data.Record
10724  * Instances of this class encapsulate both record <em>definition</em> information, and record
10725  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10726  * to access Records cached in an {@link Roo.data.Store} object.<br>
10727  * <p>
10728  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10729  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10730  * objects.<br>
10731  * <p>
10732  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10733  * @constructor
10734  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10735  * {@link #create}. The parameters are the same.
10736  * @param {Array} data An associative Array of data values keyed by the field name.
10737  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10738  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10739  * not specified an integer id is generated.
10740  */
10741 Roo.data.Record = function(data, id){
10742     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10743     this.data = data;
10744 };
10745
10746 /**
10747  * Generate a constructor for a specific record layout.
10748  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10749  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10750  * Each field definition object may contain the following properties: <ul>
10751  * <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,
10752  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10753  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10754  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10755  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10756  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10757  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10758  * this may be omitted.</p></li>
10759  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10760  * <ul><li>auto (Default, implies no conversion)</li>
10761  * <li>string</li>
10762  * <li>int</li>
10763  * <li>float</li>
10764  * <li>boolean</li>
10765  * <li>date</li></ul></p></li>
10766  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10767  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10768  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10769  * by the Reader into an object that will be stored in the Record. It is passed the
10770  * following parameters:<ul>
10771  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10772  * </ul></p></li>
10773  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10774  * </ul>
10775  * <br>usage:<br><pre><code>
10776 var TopicRecord = Roo.data.Record.create(
10777     {name: 'title', mapping: 'topic_title'},
10778     {name: 'author', mapping: 'username'},
10779     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10780     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10781     {name: 'lastPoster', mapping: 'user2'},
10782     {name: 'excerpt', mapping: 'post_text'}
10783 );
10784
10785 var myNewRecord = new TopicRecord({
10786     title: 'Do my job please',
10787     author: 'noobie',
10788     totalPosts: 1,
10789     lastPost: new Date(),
10790     lastPoster: 'Animal',
10791     excerpt: 'No way dude!'
10792 });
10793 myStore.add(myNewRecord);
10794 </code></pre>
10795  * @method create
10796  * @static
10797  */
10798 Roo.data.Record.create = function(o){
10799     var f = function(){
10800         f.superclass.constructor.apply(this, arguments);
10801     };
10802     Roo.extend(f, Roo.data.Record);
10803     var p = f.prototype;
10804     p.fields = new Roo.util.MixedCollection(false, function(field){
10805         return field.name;
10806     });
10807     for(var i = 0, len = o.length; i < len; i++){
10808         p.fields.add(new Roo.data.Field(o[i]));
10809     }
10810     f.getField = function(name){
10811         return p.fields.get(name);  
10812     };
10813     return f;
10814 };
10815
10816 Roo.data.Record.AUTO_ID = 1000;
10817 Roo.data.Record.EDIT = 'edit';
10818 Roo.data.Record.REJECT = 'reject';
10819 Roo.data.Record.COMMIT = 'commit';
10820
10821 Roo.data.Record.prototype = {
10822     /**
10823      * Readonly flag - true if this record has been modified.
10824      * @type Boolean
10825      */
10826     dirty : false,
10827     editing : false,
10828     error: null,
10829     modified: null,
10830
10831     // private
10832     join : function(store){
10833         this.store = store;
10834     },
10835
10836     /**
10837      * Set the named field to the specified value.
10838      * @param {String} name The name of the field to set.
10839      * @param {Object} value The value to set the field to.
10840      */
10841     set : function(name, value){
10842         if(this.data[name] == value){
10843             return;
10844         }
10845         this.dirty = true;
10846         if(!this.modified){
10847             this.modified = {};
10848         }
10849         if(typeof this.modified[name] == 'undefined'){
10850             this.modified[name] = this.data[name];
10851         }
10852         this.data[name] = value;
10853         if(!this.editing && this.store){
10854             this.store.afterEdit(this);
10855         }       
10856     },
10857
10858     /**
10859      * Get the value of the named field.
10860      * @param {String} name The name of the field to get the value of.
10861      * @return {Object} The value of the field.
10862      */
10863     get : function(name){
10864         return this.data[name]; 
10865     },
10866
10867     // private
10868     beginEdit : function(){
10869         this.editing = true;
10870         this.modified = {}; 
10871     },
10872
10873     // private
10874     cancelEdit : function(){
10875         this.editing = false;
10876         delete this.modified;
10877     },
10878
10879     // private
10880     endEdit : function(){
10881         this.editing = false;
10882         if(this.dirty && this.store){
10883             this.store.afterEdit(this);
10884         }
10885     },
10886
10887     /**
10888      * Usually called by the {@link Roo.data.Store} which owns the Record.
10889      * Rejects all changes made to the Record since either creation, or the last commit operation.
10890      * Modified fields are reverted to their original values.
10891      * <p>
10892      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10893      * of reject operations.
10894      */
10895     reject : function(){
10896         var m = this.modified;
10897         for(var n in m){
10898             if(typeof m[n] != "function"){
10899                 this.data[n] = m[n];
10900             }
10901         }
10902         this.dirty = false;
10903         delete this.modified;
10904         this.editing = false;
10905         if(this.store){
10906             this.store.afterReject(this);
10907         }
10908     },
10909
10910     /**
10911      * Usually called by the {@link Roo.data.Store} which owns the Record.
10912      * Commits all changes made to the Record since either creation, or the last commit operation.
10913      * <p>
10914      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10915      * of commit operations.
10916      */
10917     commit : function(){
10918         this.dirty = false;
10919         delete this.modified;
10920         this.editing = false;
10921         if(this.store){
10922             this.store.afterCommit(this);
10923         }
10924     },
10925
10926     // private
10927     hasError : function(){
10928         return this.error != null;
10929     },
10930
10931     // private
10932     clearError : function(){
10933         this.error = null;
10934     },
10935
10936     /**
10937      * Creates a copy of this record.
10938      * @param {String} id (optional) A new record id if you don't want to use this record's id
10939      * @return {Record}
10940      */
10941     copy : function(newId) {
10942         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10943     }
10944 };/*
10945  * Based on:
10946  * Ext JS Library 1.1.1
10947  * Copyright(c) 2006-2007, Ext JS, LLC.
10948  *
10949  * Originally Released Under LGPL - original licence link has changed is not relivant.
10950  *
10951  * Fork - LGPL
10952  * <script type="text/javascript">
10953  */
10954
10955
10956
10957 /**
10958  * @class Roo.data.Store
10959  * @extends Roo.util.Observable
10960  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10961  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10962  * <p>
10963  * 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
10964  * has no knowledge of the format of the data returned by the Proxy.<br>
10965  * <p>
10966  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10967  * instances from the data object. These records are cached and made available through accessor functions.
10968  * @constructor
10969  * Creates a new Store.
10970  * @param {Object} config A config object containing the objects needed for the Store to access data,
10971  * and read the data into Records.
10972  */
10973 Roo.data.Store = function(config){
10974     this.data = new Roo.util.MixedCollection(false);
10975     this.data.getKey = function(o){
10976         return o.id;
10977     };
10978     this.baseParams = {};
10979     // private
10980     this.paramNames = {
10981         "start" : "start",
10982         "limit" : "limit",
10983         "sort" : "sort",
10984         "dir" : "dir",
10985         "multisort" : "_multisort"
10986     };
10987
10988     if(config && config.data){
10989         this.inlineData = config.data;
10990         delete config.data;
10991     }
10992
10993     Roo.apply(this, config);
10994     
10995     if(this.reader){ // reader passed
10996         this.reader = Roo.factory(this.reader, Roo.data);
10997         this.reader.xmodule = this.xmodule || false;
10998         if(!this.recordType){
10999             this.recordType = this.reader.recordType;
11000         }
11001         if(this.reader.onMetaChange){
11002             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11003         }
11004     }
11005
11006     if(this.recordType){
11007         this.fields = this.recordType.prototype.fields;
11008     }
11009     this.modified = [];
11010
11011     this.addEvents({
11012         /**
11013          * @event datachanged
11014          * Fires when the data cache has changed, and a widget which is using this Store
11015          * as a Record cache should refresh its view.
11016          * @param {Store} this
11017          */
11018         datachanged : true,
11019         /**
11020          * @event metachange
11021          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11022          * @param {Store} this
11023          * @param {Object} meta The JSON metadata
11024          */
11025         metachange : true,
11026         /**
11027          * @event add
11028          * Fires when Records have been added to the Store
11029          * @param {Store} this
11030          * @param {Roo.data.Record[]} records The array of Records added
11031          * @param {Number} index The index at which the record(s) were added
11032          */
11033         add : true,
11034         /**
11035          * @event remove
11036          * Fires when a Record has been removed from the Store
11037          * @param {Store} this
11038          * @param {Roo.data.Record} record The Record that was removed
11039          * @param {Number} index The index at which the record was removed
11040          */
11041         remove : true,
11042         /**
11043          * @event update
11044          * Fires when a Record has been updated
11045          * @param {Store} this
11046          * @param {Roo.data.Record} record The Record that was updated
11047          * @param {String} operation The update operation being performed.  Value may be one of:
11048          * <pre><code>
11049  Roo.data.Record.EDIT
11050  Roo.data.Record.REJECT
11051  Roo.data.Record.COMMIT
11052          * </code></pre>
11053          */
11054         update : true,
11055         /**
11056          * @event clear
11057          * Fires when the data cache has been cleared.
11058          * @param {Store} this
11059          */
11060         clear : true,
11061         /**
11062          * @event beforeload
11063          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11064          * the load action will be canceled.
11065          * @param {Store} this
11066          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11067          */
11068         beforeload : true,
11069         /**
11070          * @event beforeloadadd
11071          * Fires after a new set of Records has been loaded.
11072          * @param {Store} this
11073          * @param {Roo.data.Record[]} records The Records that were loaded
11074          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11075          */
11076         beforeloadadd : true,
11077         /**
11078          * @event load
11079          * Fires after a new set of Records has been loaded, before they are added to the store.
11080          * @param {Store} this
11081          * @param {Roo.data.Record[]} records The Records that were loaded
11082          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11083          * @params {Object} return from reader
11084          */
11085         load : true,
11086         /**
11087          * @event loadexception
11088          * Fires if an exception occurs in the Proxy during loading.
11089          * Called with the signature of the Proxy's "loadexception" event.
11090          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11091          * 
11092          * @param {Proxy} 
11093          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11094          * @param {Object} load options 
11095          * @param {Object} jsonData from your request (normally this contains the Exception)
11096          */
11097         loadexception : true
11098     });
11099     
11100     if(this.proxy){
11101         this.proxy = Roo.factory(this.proxy, Roo.data);
11102         this.proxy.xmodule = this.xmodule || false;
11103         this.relayEvents(this.proxy,  ["loadexception"]);
11104     }
11105     this.sortToggle = {};
11106     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11107
11108     Roo.data.Store.superclass.constructor.call(this);
11109
11110     if(this.inlineData){
11111         this.loadData(this.inlineData);
11112         delete this.inlineData;
11113     }
11114 };
11115
11116 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11117      /**
11118     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11119     * without a remote query - used by combo/forms at present.
11120     */
11121     
11122     /**
11123     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11124     */
11125     /**
11126     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11127     */
11128     /**
11129     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11130     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11131     */
11132     /**
11133     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11134     * on any HTTP request
11135     */
11136     /**
11137     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11138     */
11139     /**
11140     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11141     */
11142     multiSort: false,
11143     /**
11144     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11145     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11146     */
11147     remoteSort : false,
11148
11149     /**
11150     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11151      * loaded or when a record is removed. (defaults to false).
11152     */
11153     pruneModifiedRecords : false,
11154
11155     // private
11156     lastOptions : null,
11157
11158     /**
11159      * Add Records to the Store and fires the add event.
11160      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11161      */
11162     add : function(records){
11163         records = [].concat(records);
11164         for(var i = 0, len = records.length; i < len; i++){
11165             records[i].join(this);
11166         }
11167         var index = this.data.length;
11168         this.data.addAll(records);
11169         this.fireEvent("add", this, records, index);
11170     },
11171
11172     /**
11173      * Remove a Record from the Store and fires the remove event.
11174      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11175      */
11176     remove : function(record){
11177         var index = this.data.indexOf(record);
11178         this.data.removeAt(index);
11179  
11180         if(this.pruneModifiedRecords){
11181             this.modified.remove(record);
11182         }
11183         this.fireEvent("remove", this, record, index);
11184     },
11185
11186     /**
11187      * Remove all Records from the Store and fires the clear event.
11188      */
11189     removeAll : function(){
11190         this.data.clear();
11191         if(this.pruneModifiedRecords){
11192             this.modified = [];
11193         }
11194         this.fireEvent("clear", this);
11195     },
11196
11197     /**
11198      * Inserts Records to the Store at the given index and fires the add event.
11199      * @param {Number} index The start index at which to insert the passed Records.
11200      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11201      */
11202     insert : function(index, records){
11203         records = [].concat(records);
11204         for(var i = 0, len = records.length; i < len; i++){
11205             this.data.insert(index, records[i]);
11206             records[i].join(this);
11207         }
11208         this.fireEvent("add", this, records, index);
11209     },
11210
11211     /**
11212      * Get the index within the cache of the passed Record.
11213      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11214      * @return {Number} The index of the passed Record. Returns -1 if not found.
11215      */
11216     indexOf : function(record){
11217         return this.data.indexOf(record);
11218     },
11219
11220     /**
11221      * Get the index within the cache of the Record with the passed id.
11222      * @param {String} id The id of the Record to find.
11223      * @return {Number} The index of the Record. Returns -1 if not found.
11224      */
11225     indexOfId : function(id){
11226         return this.data.indexOfKey(id);
11227     },
11228
11229     /**
11230      * Get the Record with the specified id.
11231      * @param {String} id The id of the Record to find.
11232      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11233      */
11234     getById : function(id){
11235         return this.data.key(id);
11236     },
11237
11238     /**
11239      * Get the Record at the specified index.
11240      * @param {Number} index The index of the Record to find.
11241      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11242      */
11243     getAt : function(index){
11244         return this.data.itemAt(index);
11245     },
11246
11247     /**
11248      * Returns a range of Records between specified indices.
11249      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11250      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11251      * @return {Roo.data.Record[]} An array of Records
11252      */
11253     getRange : function(start, end){
11254         return this.data.getRange(start, end);
11255     },
11256
11257     // private
11258     storeOptions : function(o){
11259         o = Roo.apply({}, o);
11260         delete o.callback;
11261         delete o.scope;
11262         this.lastOptions = o;
11263     },
11264
11265     /**
11266      * Loads the Record cache from the configured Proxy using the configured Reader.
11267      * <p>
11268      * If using remote paging, then the first load call must specify the <em>start</em>
11269      * and <em>limit</em> properties in the options.params property to establish the initial
11270      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11271      * <p>
11272      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11273      * and this call will return before the new data has been loaded. Perform any post-processing
11274      * in a callback function, or in a "load" event handler.</strong>
11275      * <p>
11276      * @param {Object} options An object containing properties which control loading options:<ul>
11277      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11278      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11279      * passed the following arguments:<ul>
11280      * <li>r : Roo.data.Record[]</li>
11281      * <li>options: Options object from the load call</li>
11282      * <li>success: Boolean success indicator</li></ul></li>
11283      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11284      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11285      * </ul>
11286      */
11287     load : function(options){
11288         options = options || {};
11289         if(this.fireEvent("beforeload", this, options) !== false){
11290             this.storeOptions(options);
11291             var p = Roo.apply(options.params || {}, this.baseParams);
11292             // if meta was not loaded from remote source.. try requesting it.
11293             if (!this.reader.metaFromRemote) {
11294                 p._requestMeta = 1;
11295             }
11296             if(this.sortInfo && this.remoteSort){
11297                 var pn = this.paramNames;
11298                 p[pn["sort"]] = this.sortInfo.field;
11299                 p[pn["dir"]] = this.sortInfo.direction;
11300             }
11301             if (this.multiSort) {
11302                 var pn = this.paramNames;
11303                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11304             }
11305             
11306             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11307         }
11308     },
11309
11310     /**
11311      * Reloads the Record cache from the configured Proxy using the configured Reader and
11312      * the options from the last load operation performed.
11313      * @param {Object} options (optional) An object containing properties which may override the options
11314      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11315      * the most recently used options are reused).
11316      */
11317     reload : function(options){
11318         this.load(Roo.applyIf(options||{}, this.lastOptions));
11319     },
11320
11321     // private
11322     // Called as a callback by the Reader during a load operation.
11323     loadRecords : function(o, options, success){
11324         if(!o || success === false){
11325             if(success !== false){
11326                 this.fireEvent("load", this, [], options, o);
11327             }
11328             if(options.callback){
11329                 options.callback.call(options.scope || this, [], options, false);
11330             }
11331             return;
11332         }
11333         // if data returned failure - throw an exception.
11334         if (o.success === false) {
11335             // show a message if no listener is registered.
11336             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11337                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11338             }
11339             // loadmask wil be hooked into this..
11340             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11341             return;
11342         }
11343         var r = o.records, t = o.totalRecords || r.length;
11344         
11345         this.fireEvent("beforeloadadd", this, r, options, o);
11346         
11347         if(!options || options.add !== true){
11348             if(this.pruneModifiedRecords){
11349                 this.modified = [];
11350             }
11351             for(var i = 0, len = r.length; i < len; i++){
11352                 r[i].join(this);
11353             }
11354             if(this.snapshot){
11355                 this.data = this.snapshot;
11356                 delete this.snapshot;
11357             }
11358             this.data.clear();
11359             this.data.addAll(r);
11360             this.totalLength = t;
11361             this.applySort();
11362             this.fireEvent("datachanged", this);
11363         }else{
11364             this.totalLength = Math.max(t, this.data.length+r.length);
11365             this.add(r);
11366         }
11367         
11368         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11369                 
11370             var e = new Roo.data.Record({});
11371
11372             e.set(this.parent.displayField, this.parent.emptyTitle);
11373             e.set(this.parent.valueField, '');
11374
11375             this.insert(0, e);
11376         }
11377             
11378         this.fireEvent("load", this, r, options, o);
11379         if(options.callback){
11380             options.callback.call(options.scope || this, r, options, true);
11381         }
11382     },
11383
11384
11385     /**
11386      * Loads data from a passed data block. A Reader which understands the format of the data
11387      * must have been configured in the constructor.
11388      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11389      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11390      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11391      */
11392     loadData : function(o, append){
11393         var r = this.reader.readRecords(o);
11394         this.loadRecords(r, {add: append}, true);
11395     },
11396
11397     /**
11398      * Gets the number of cached records.
11399      * <p>
11400      * <em>If using paging, this may not be the total size of the dataset. If the data object
11401      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11402      * the data set size</em>
11403      */
11404     getCount : function(){
11405         return this.data.length || 0;
11406     },
11407
11408     /**
11409      * Gets the total number of records in the dataset as returned by the server.
11410      * <p>
11411      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11412      * the dataset size</em>
11413      */
11414     getTotalCount : function(){
11415         return this.totalLength || 0;
11416     },
11417
11418     /**
11419      * Returns the sort state of the Store as an object with two properties:
11420      * <pre><code>
11421  field {String} The name of the field by which the Records are sorted
11422  direction {String} The sort order, "ASC" or "DESC"
11423      * </code></pre>
11424      */
11425     getSortState : function(){
11426         return this.sortInfo;
11427     },
11428
11429     // private
11430     applySort : function(){
11431         if(this.sortInfo && !this.remoteSort){
11432             var s = this.sortInfo, f = s.field;
11433             var st = this.fields.get(f).sortType;
11434             var fn = function(r1, r2){
11435                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11436                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11437             };
11438             this.data.sort(s.direction, fn);
11439             if(this.snapshot && this.snapshot != this.data){
11440                 this.snapshot.sort(s.direction, fn);
11441             }
11442         }
11443     },
11444
11445     /**
11446      * Sets the default sort column and order to be used by the next load operation.
11447      * @param {String} fieldName The name of the field to sort by.
11448      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11449      */
11450     setDefaultSort : function(field, dir){
11451         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11452     },
11453
11454     /**
11455      * Sort the Records.
11456      * If remote sorting is used, the sort is performed on the server, and the cache is
11457      * reloaded. If local sorting is used, the cache is sorted internally.
11458      * @param {String} fieldName The name of the field to sort by.
11459      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11460      */
11461     sort : function(fieldName, dir){
11462         var f = this.fields.get(fieldName);
11463         if(!dir){
11464             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11465             
11466             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11467                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11468             }else{
11469                 dir = f.sortDir;
11470             }
11471         }
11472         this.sortToggle[f.name] = dir;
11473         this.sortInfo = {field: f.name, direction: dir};
11474         if(!this.remoteSort){
11475             this.applySort();
11476             this.fireEvent("datachanged", this);
11477         }else{
11478             this.load(this.lastOptions);
11479         }
11480     },
11481
11482     /**
11483      * Calls the specified function for each of the Records in the cache.
11484      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11485      * Returning <em>false</em> aborts and exits the iteration.
11486      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11487      */
11488     each : function(fn, scope){
11489         this.data.each(fn, scope);
11490     },
11491
11492     /**
11493      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11494      * (e.g., during paging).
11495      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11496      */
11497     getModifiedRecords : function(){
11498         return this.modified;
11499     },
11500
11501     // private
11502     createFilterFn : function(property, value, anyMatch){
11503         if(!value.exec){ // not a regex
11504             value = String(value);
11505             if(value.length == 0){
11506                 return false;
11507             }
11508             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11509         }
11510         return function(r){
11511             return value.test(r.data[property]);
11512         };
11513     },
11514
11515     /**
11516      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11517      * @param {String} property A field on your records
11518      * @param {Number} start The record index to start at (defaults to 0)
11519      * @param {Number} end The last record index to include (defaults to length - 1)
11520      * @return {Number} The sum
11521      */
11522     sum : function(property, start, end){
11523         var rs = this.data.items, v = 0;
11524         start = start || 0;
11525         end = (end || end === 0) ? end : rs.length-1;
11526
11527         for(var i = start; i <= end; i++){
11528             v += (rs[i].data[property] || 0);
11529         }
11530         return v;
11531     },
11532
11533     /**
11534      * Filter the records by a specified property.
11535      * @param {String} field A field on your records
11536      * @param {String/RegExp} value Either a string that the field
11537      * should start with or a RegExp to test against the field
11538      * @param {Boolean} anyMatch True to match any part not just the beginning
11539      */
11540     filter : function(property, value, anyMatch){
11541         var fn = this.createFilterFn(property, value, anyMatch);
11542         return fn ? this.filterBy(fn) : this.clearFilter();
11543     },
11544
11545     /**
11546      * Filter by a function. The specified function will be called with each
11547      * record in this data source. If the function returns true the record is included,
11548      * otherwise it is filtered.
11549      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11550      * @param {Object} scope (optional) The scope of the function (defaults to this)
11551      */
11552     filterBy : function(fn, scope){
11553         this.snapshot = this.snapshot || this.data;
11554         this.data = this.queryBy(fn, scope||this);
11555         this.fireEvent("datachanged", this);
11556     },
11557
11558     /**
11559      * Query the records by a specified property.
11560      * @param {String} field A field on your records
11561      * @param {String/RegExp} value Either a string that the field
11562      * should start with or a RegExp to test against the field
11563      * @param {Boolean} anyMatch True to match any part not just the beginning
11564      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11565      */
11566     query : function(property, value, anyMatch){
11567         var fn = this.createFilterFn(property, value, anyMatch);
11568         return fn ? this.queryBy(fn) : this.data.clone();
11569     },
11570
11571     /**
11572      * Query by a function. The specified function will be called with each
11573      * record in this data source. If the function returns true the record is included
11574      * in the results.
11575      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11576      * @param {Object} scope (optional) The scope of the function (defaults to this)
11577       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11578      **/
11579     queryBy : function(fn, scope){
11580         var data = this.snapshot || this.data;
11581         return data.filterBy(fn, scope||this);
11582     },
11583
11584     /**
11585      * Collects unique values for a particular dataIndex from this store.
11586      * @param {String} dataIndex The property to collect
11587      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11588      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11589      * @return {Array} An array of the unique values
11590      **/
11591     collect : function(dataIndex, allowNull, bypassFilter){
11592         var d = (bypassFilter === true && this.snapshot) ?
11593                 this.snapshot.items : this.data.items;
11594         var v, sv, r = [], l = {};
11595         for(var i = 0, len = d.length; i < len; i++){
11596             v = d[i].data[dataIndex];
11597             sv = String(v);
11598             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11599                 l[sv] = true;
11600                 r[r.length] = v;
11601             }
11602         }
11603         return r;
11604     },
11605
11606     /**
11607      * Revert to a view of the Record cache with no filtering applied.
11608      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11609      */
11610     clearFilter : function(suppressEvent){
11611         if(this.snapshot && this.snapshot != this.data){
11612             this.data = this.snapshot;
11613             delete this.snapshot;
11614             if(suppressEvent !== true){
11615                 this.fireEvent("datachanged", this);
11616             }
11617         }
11618     },
11619
11620     // private
11621     afterEdit : function(record){
11622         if(this.modified.indexOf(record) == -1){
11623             this.modified.push(record);
11624         }
11625         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11626     },
11627     
11628     // private
11629     afterReject : function(record){
11630         this.modified.remove(record);
11631         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11632     },
11633
11634     // private
11635     afterCommit : function(record){
11636         this.modified.remove(record);
11637         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11638     },
11639
11640     /**
11641      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11642      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11643      */
11644     commitChanges : function(){
11645         var m = this.modified.slice(0);
11646         this.modified = [];
11647         for(var i = 0, len = m.length; i < len; i++){
11648             m[i].commit();
11649         }
11650     },
11651
11652     /**
11653      * Cancel outstanding changes on all changed records.
11654      */
11655     rejectChanges : function(){
11656         var m = this.modified.slice(0);
11657         this.modified = [];
11658         for(var i = 0, len = m.length; i < len; i++){
11659             m[i].reject();
11660         }
11661     },
11662
11663     onMetaChange : function(meta, rtype, o){
11664         this.recordType = rtype;
11665         this.fields = rtype.prototype.fields;
11666         delete this.snapshot;
11667         this.sortInfo = meta.sortInfo || this.sortInfo;
11668         this.modified = [];
11669         this.fireEvent('metachange', this, this.reader.meta);
11670     },
11671     
11672     moveIndex : function(data, type)
11673     {
11674         var index = this.indexOf(data);
11675         
11676         var newIndex = index + type;
11677         
11678         this.remove(data);
11679         
11680         this.insert(newIndex, data);
11681         
11682     }
11683 });/*
11684  * Based on:
11685  * Ext JS Library 1.1.1
11686  * Copyright(c) 2006-2007, Ext JS, LLC.
11687  *
11688  * Originally Released Under LGPL - original licence link has changed is not relivant.
11689  *
11690  * Fork - LGPL
11691  * <script type="text/javascript">
11692  */
11693
11694 /**
11695  * @class Roo.data.SimpleStore
11696  * @extends Roo.data.Store
11697  * Small helper class to make creating Stores from Array data easier.
11698  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11699  * @cfg {Array} fields An array of field definition objects, or field name strings.
11700  * @cfg {Array} data The multi-dimensional array of data
11701  * @constructor
11702  * @param {Object} config
11703  */
11704 Roo.data.SimpleStore = function(config){
11705     Roo.data.SimpleStore.superclass.constructor.call(this, {
11706         isLocal : true,
11707         reader: new Roo.data.ArrayReader({
11708                 id: config.id
11709             },
11710             Roo.data.Record.create(config.fields)
11711         ),
11712         proxy : new Roo.data.MemoryProxy(config.data)
11713     });
11714     this.load();
11715 };
11716 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11717  * Based on:
11718  * Ext JS Library 1.1.1
11719  * Copyright(c) 2006-2007, Ext JS, LLC.
11720  *
11721  * Originally Released Under LGPL - original licence link has changed is not relivant.
11722  *
11723  * Fork - LGPL
11724  * <script type="text/javascript">
11725  */
11726
11727 /**
11728 /**
11729  * @extends Roo.data.Store
11730  * @class Roo.data.JsonStore
11731  * Small helper class to make creating Stores for JSON data easier. <br/>
11732 <pre><code>
11733 var store = new Roo.data.JsonStore({
11734     url: 'get-images.php',
11735     root: 'images',
11736     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11737 });
11738 </code></pre>
11739  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11740  * JsonReader and HttpProxy (unless inline data is provided).</b>
11741  * @cfg {Array} fields An array of field definition objects, or field name strings.
11742  * @constructor
11743  * @param {Object} config
11744  */
11745 Roo.data.JsonStore = function(c){
11746     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11747         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11748         reader: new Roo.data.JsonReader(c, c.fields)
11749     }));
11750 };
11751 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11752  * Based on:
11753  * Ext JS Library 1.1.1
11754  * Copyright(c) 2006-2007, Ext JS, LLC.
11755  *
11756  * Originally Released Under LGPL - original licence link has changed is not relivant.
11757  *
11758  * Fork - LGPL
11759  * <script type="text/javascript">
11760  */
11761
11762  
11763 Roo.data.Field = function(config){
11764     if(typeof config == "string"){
11765         config = {name: config};
11766     }
11767     Roo.apply(this, config);
11768     
11769     if(!this.type){
11770         this.type = "auto";
11771     }
11772     
11773     var st = Roo.data.SortTypes;
11774     // named sortTypes are supported, here we look them up
11775     if(typeof this.sortType == "string"){
11776         this.sortType = st[this.sortType];
11777     }
11778     
11779     // set default sortType for strings and dates
11780     if(!this.sortType){
11781         switch(this.type){
11782             case "string":
11783                 this.sortType = st.asUCString;
11784                 break;
11785             case "date":
11786                 this.sortType = st.asDate;
11787                 break;
11788             default:
11789                 this.sortType = st.none;
11790         }
11791     }
11792
11793     // define once
11794     var stripRe = /[\$,%]/g;
11795
11796     // prebuilt conversion function for this field, instead of
11797     // switching every time we're reading a value
11798     if(!this.convert){
11799         var cv, dateFormat = this.dateFormat;
11800         switch(this.type){
11801             case "":
11802             case "auto":
11803             case undefined:
11804                 cv = function(v){ return v; };
11805                 break;
11806             case "string":
11807                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11808                 break;
11809             case "int":
11810                 cv = function(v){
11811                     return v !== undefined && v !== null && v !== '' ?
11812                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11813                     };
11814                 break;
11815             case "float":
11816                 cv = function(v){
11817                     return v !== undefined && v !== null && v !== '' ?
11818                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11819                     };
11820                 break;
11821             case "bool":
11822             case "boolean":
11823                 cv = function(v){ return v === true || v === "true" || v == 1; };
11824                 break;
11825             case "date":
11826                 cv = function(v){
11827                     if(!v){
11828                         return '';
11829                     }
11830                     if(v instanceof Date){
11831                         return v;
11832                     }
11833                     if(dateFormat){
11834                         if(dateFormat == "timestamp"){
11835                             return new Date(v*1000);
11836                         }
11837                         return Date.parseDate(v, dateFormat);
11838                     }
11839                     var parsed = Date.parse(v);
11840                     return parsed ? new Date(parsed) : null;
11841                 };
11842              break;
11843             
11844         }
11845         this.convert = cv;
11846     }
11847 };
11848
11849 Roo.data.Field.prototype = {
11850     dateFormat: null,
11851     defaultValue: "",
11852     mapping: null,
11853     sortType : null,
11854     sortDir : "ASC"
11855 };/*
11856  * Based on:
11857  * Ext JS Library 1.1.1
11858  * Copyright(c) 2006-2007, Ext JS, LLC.
11859  *
11860  * Originally Released Under LGPL - original licence link has changed is not relivant.
11861  *
11862  * Fork - LGPL
11863  * <script type="text/javascript">
11864  */
11865  
11866 // Base class for reading structured data from a data source.  This class is intended to be
11867 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11868
11869 /**
11870  * @class Roo.data.DataReader
11871  * Base class for reading structured data from a data source.  This class is intended to be
11872  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11873  */
11874
11875 Roo.data.DataReader = function(meta, recordType){
11876     
11877     this.meta = meta;
11878     
11879     this.recordType = recordType instanceof Array ? 
11880         Roo.data.Record.create(recordType) : recordType;
11881 };
11882
11883 Roo.data.DataReader.prototype = {
11884      /**
11885      * Create an empty record
11886      * @param {Object} data (optional) - overlay some values
11887      * @return {Roo.data.Record} record created.
11888      */
11889     newRow :  function(d) {
11890         var da =  {};
11891         this.recordType.prototype.fields.each(function(c) {
11892             switch( c.type) {
11893                 case 'int' : da[c.name] = 0; break;
11894                 case 'date' : da[c.name] = new Date(); break;
11895                 case 'float' : da[c.name] = 0.0; break;
11896                 case 'boolean' : da[c.name] = false; break;
11897                 default : da[c.name] = ""; break;
11898             }
11899             
11900         });
11901         return new this.recordType(Roo.apply(da, d));
11902     }
11903     
11904 };/*
11905  * Based on:
11906  * Ext JS Library 1.1.1
11907  * Copyright(c) 2006-2007, Ext JS, LLC.
11908  *
11909  * Originally Released Under LGPL - original licence link has changed is not relivant.
11910  *
11911  * Fork - LGPL
11912  * <script type="text/javascript">
11913  */
11914
11915 /**
11916  * @class Roo.data.DataProxy
11917  * @extends Roo.data.Observable
11918  * This class is an abstract base class for implementations which provide retrieval of
11919  * unformatted data objects.<br>
11920  * <p>
11921  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11922  * (of the appropriate type which knows how to parse the data object) to provide a block of
11923  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11924  * <p>
11925  * Custom implementations must implement the load method as described in
11926  * {@link Roo.data.HttpProxy#load}.
11927  */
11928 Roo.data.DataProxy = function(){
11929     this.addEvents({
11930         /**
11931          * @event beforeload
11932          * Fires before a network request is made to retrieve a data object.
11933          * @param {Object} This DataProxy object.
11934          * @param {Object} params The params parameter to the load function.
11935          */
11936         beforeload : true,
11937         /**
11938          * @event load
11939          * Fires before the load method's callback is called.
11940          * @param {Object} This DataProxy object.
11941          * @param {Object} o The data object.
11942          * @param {Object} arg The callback argument object passed to the load function.
11943          */
11944         load : true,
11945         /**
11946          * @event loadexception
11947          * Fires if an Exception occurs during data retrieval.
11948          * @param {Object} This DataProxy object.
11949          * @param {Object} o The data object.
11950          * @param {Object} arg The callback argument object passed to the load function.
11951          * @param {Object} e The Exception.
11952          */
11953         loadexception : true
11954     });
11955     Roo.data.DataProxy.superclass.constructor.call(this);
11956 };
11957
11958 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11959
11960     /**
11961      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11962      */
11963 /*
11964  * Based on:
11965  * Ext JS Library 1.1.1
11966  * Copyright(c) 2006-2007, Ext JS, LLC.
11967  *
11968  * Originally Released Under LGPL - original licence link has changed is not relivant.
11969  *
11970  * Fork - LGPL
11971  * <script type="text/javascript">
11972  */
11973 /**
11974  * @class Roo.data.MemoryProxy
11975  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11976  * to the Reader when its load method is called.
11977  * @constructor
11978  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11979  */
11980 Roo.data.MemoryProxy = function(data){
11981     if (data.data) {
11982         data = data.data;
11983     }
11984     Roo.data.MemoryProxy.superclass.constructor.call(this);
11985     this.data = data;
11986 };
11987
11988 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11989     
11990     /**
11991      * Load data from the requested source (in this case an in-memory
11992      * data object passed to the constructor), read the data object into
11993      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11994      * process that block using the passed callback.
11995      * @param {Object} params This parameter is not used by the MemoryProxy class.
11996      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11997      * object into a block of Roo.data.Records.
11998      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11999      * The function must be passed <ul>
12000      * <li>The Record block object</li>
12001      * <li>The "arg" argument from the load function</li>
12002      * <li>A boolean success indicator</li>
12003      * </ul>
12004      * @param {Object} scope The scope in which to call the callback
12005      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12006      */
12007     load : function(params, reader, callback, scope, arg){
12008         params = params || {};
12009         var result;
12010         try {
12011             result = reader.readRecords(this.data);
12012         }catch(e){
12013             this.fireEvent("loadexception", this, arg, null, e);
12014             callback.call(scope, null, arg, false);
12015             return;
12016         }
12017         callback.call(scope, result, arg, true);
12018     },
12019     
12020     // private
12021     update : function(params, records){
12022         
12023     }
12024 });/*
12025  * Based on:
12026  * Ext JS Library 1.1.1
12027  * Copyright(c) 2006-2007, Ext JS, LLC.
12028  *
12029  * Originally Released Under LGPL - original licence link has changed is not relivant.
12030  *
12031  * Fork - LGPL
12032  * <script type="text/javascript">
12033  */
12034 /**
12035  * @class Roo.data.HttpProxy
12036  * @extends Roo.data.DataProxy
12037  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12038  * configured to reference a certain URL.<br><br>
12039  * <p>
12040  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12041  * from which the running page was served.<br><br>
12042  * <p>
12043  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12044  * <p>
12045  * Be aware that to enable the browser to parse an XML document, the server must set
12046  * the Content-Type header in the HTTP response to "text/xml".
12047  * @constructor
12048  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12049  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12050  * will be used to make the request.
12051  */
12052 Roo.data.HttpProxy = function(conn){
12053     Roo.data.HttpProxy.superclass.constructor.call(this);
12054     // is conn a conn config or a real conn?
12055     this.conn = conn;
12056     this.useAjax = !conn || !conn.events;
12057   
12058 };
12059
12060 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12061     // thse are take from connection...
12062     
12063     /**
12064      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12065      */
12066     /**
12067      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12068      * extra parameters to each request made by this object. (defaults to undefined)
12069      */
12070     /**
12071      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12072      *  to each request made by this object. (defaults to undefined)
12073      */
12074     /**
12075      * @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)
12076      */
12077     /**
12078      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12079      */
12080      /**
12081      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12082      * @type Boolean
12083      */
12084   
12085
12086     /**
12087      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12088      * @type Boolean
12089      */
12090     /**
12091      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12092      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12093      * a finer-grained basis than the DataProxy events.
12094      */
12095     getConnection : function(){
12096         return this.useAjax ? Roo.Ajax : this.conn;
12097     },
12098
12099     /**
12100      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12101      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12102      * process that block using the passed callback.
12103      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12104      * for the request to the remote server.
12105      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12106      * object into a block of Roo.data.Records.
12107      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12108      * The function must be passed <ul>
12109      * <li>The Record block object</li>
12110      * <li>The "arg" argument from the load function</li>
12111      * <li>A boolean success indicator</li>
12112      * </ul>
12113      * @param {Object} scope The scope in which to call the callback
12114      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12115      */
12116     load : function(params, reader, callback, scope, arg){
12117         if(this.fireEvent("beforeload", this, params) !== false){
12118             var  o = {
12119                 params : params || {},
12120                 request: {
12121                     callback : callback,
12122                     scope : scope,
12123                     arg : arg
12124                 },
12125                 reader: reader,
12126                 callback : this.loadResponse,
12127                 scope: this
12128             };
12129             if(this.useAjax){
12130                 Roo.applyIf(o, this.conn);
12131                 if(this.activeRequest){
12132                     Roo.Ajax.abort(this.activeRequest);
12133                 }
12134                 this.activeRequest = Roo.Ajax.request(o);
12135             }else{
12136                 this.conn.request(o);
12137             }
12138         }else{
12139             callback.call(scope||this, null, arg, false);
12140         }
12141     },
12142
12143     // private
12144     loadResponse : function(o, success, response){
12145         delete this.activeRequest;
12146         if(!success){
12147             this.fireEvent("loadexception", this, o, response);
12148             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12149             return;
12150         }
12151         var result;
12152         try {
12153             result = o.reader.read(response);
12154         }catch(e){
12155             this.fireEvent("loadexception", this, o, response, e);
12156             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12157             return;
12158         }
12159         
12160         this.fireEvent("load", this, o, o.request.arg);
12161         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12162     },
12163
12164     // private
12165     update : function(dataSet){
12166
12167     },
12168
12169     // private
12170     updateResponse : function(dataSet){
12171
12172     }
12173 });/*
12174  * Based on:
12175  * Ext JS Library 1.1.1
12176  * Copyright(c) 2006-2007, Ext JS, LLC.
12177  *
12178  * Originally Released Under LGPL - original licence link has changed is not relivant.
12179  *
12180  * Fork - LGPL
12181  * <script type="text/javascript">
12182  */
12183
12184 /**
12185  * @class Roo.data.ScriptTagProxy
12186  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12187  * other than the originating domain of the running page.<br><br>
12188  * <p>
12189  * <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
12190  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12191  * <p>
12192  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12193  * source code that is used as the source inside a &lt;script> tag.<br><br>
12194  * <p>
12195  * In order for the browser to process the returned data, the server must wrap the data object
12196  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12197  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12198  * depending on whether the callback name was passed:
12199  * <p>
12200  * <pre><code>
12201 boolean scriptTag = false;
12202 String cb = request.getParameter("callback");
12203 if (cb != null) {
12204     scriptTag = true;
12205     response.setContentType("text/javascript");
12206 } else {
12207     response.setContentType("application/x-json");
12208 }
12209 Writer out = response.getWriter();
12210 if (scriptTag) {
12211     out.write(cb + "(");
12212 }
12213 out.print(dataBlock.toJsonString());
12214 if (scriptTag) {
12215     out.write(");");
12216 }
12217 </pre></code>
12218  *
12219  * @constructor
12220  * @param {Object} config A configuration object.
12221  */
12222 Roo.data.ScriptTagProxy = function(config){
12223     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12224     Roo.apply(this, config);
12225     this.head = document.getElementsByTagName("head")[0];
12226 };
12227
12228 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12229
12230 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12231     /**
12232      * @cfg {String} url The URL from which to request the data object.
12233      */
12234     /**
12235      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12236      */
12237     timeout : 30000,
12238     /**
12239      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12240      * the server the name of the callback function set up by the load call to process the returned data object.
12241      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12242      * javascript output which calls this named function passing the data object as its only parameter.
12243      */
12244     callbackParam : "callback",
12245     /**
12246      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12247      * name to the request.
12248      */
12249     nocache : true,
12250
12251     /**
12252      * Load data from the configured URL, read the data object into
12253      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12254      * process that block using the passed callback.
12255      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12256      * for the request to the remote server.
12257      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12258      * object into a block of Roo.data.Records.
12259      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12260      * The function must be passed <ul>
12261      * <li>The Record block object</li>
12262      * <li>The "arg" argument from the load function</li>
12263      * <li>A boolean success indicator</li>
12264      * </ul>
12265      * @param {Object} scope The scope in which to call the callback
12266      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12267      */
12268     load : function(params, reader, callback, scope, arg){
12269         if(this.fireEvent("beforeload", this, params) !== false){
12270
12271             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12272
12273             var url = this.url;
12274             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12275             if(this.nocache){
12276                 url += "&_dc=" + (new Date().getTime());
12277             }
12278             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12279             var trans = {
12280                 id : transId,
12281                 cb : "stcCallback"+transId,
12282                 scriptId : "stcScript"+transId,
12283                 params : params,
12284                 arg : arg,
12285                 url : url,
12286                 callback : callback,
12287                 scope : scope,
12288                 reader : reader
12289             };
12290             var conn = this;
12291
12292             window[trans.cb] = function(o){
12293                 conn.handleResponse(o, trans);
12294             };
12295
12296             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12297
12298             if(this.autoAbort !== false){
12299                 this.abort();
12300             }
12301
12302             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12303
12304             var script = document.createElement("script");
12305             script.setAttribute("src", url);
12306             script.setAttribute("type", "text/javascript");
12307             script.setAttribute("id", trans.scriptId);
12308             this.head.appendChild(script);
12309
12310             this.trans = trans;
12311         }else{
12312             callback.call(scope||this, null, arg, false);
12313         }
12314     },
12315
12316     // private
12317     isLoading : function(){
12318         return this.trans ? true : false;
12319     },
12320
12321     /**
12322      * Abort the current server request.
12323      */
12324     abort : function(){
12325         if(this.isLoading()){
12326             this.destroyTrans(this.trans);
12327         }
12328     },
12329
12330     // private
12331     destroyTrans : function(trans, isLoaded){
12332         this.head.removeChild(document.getElementById(trans.scriptId));
12333         clearTimeout(trans.timeoutId);
12334         if(isLoaded){
12335             window[trans.cb] = undefined;
12336             try{
12337                 delete window[trans.cb];
12338             }catch(e){}
12339         }else{
12340             // if hasn't been loaded, wait for load to remove it to prevent script error
12341             window[trans.cb] = function(){
12342                 window[trans.cb] = undefined;
12343                 try{
12344                     delete window[trans.cb];
12345                 }catch(e){}
12346             };
12347         }
12348     },
12349
12350     // private
12351     handleResponse : function(o, trans){
12352         this.trans = false;
12353         this.destroyTrans(trans, true);
12354         var result;
12355         try {
12356             result = trans.reader.readRecords(o);
12357         }catch(e){
12358             this.fireEvent("loadexception", this, o, trans.arg, e);
12359             trans.callback.call(trans.scope||window, null, trans.arg, false);
12360             return;
12361         }
12362         this.fireEvent("load", this, o, trans.arg);
12363         trans.callback.call(trans.scope||window, result, trans.arg, true);
12364     },
12365
12366     // private
12367     handleFailure : function(trans){
12368         this.trans = false;
12369         this.destroyTrans(trans, false);
12370         this.fireEvent("loadexception", this, null, trans.arg);
12371         trans.callback.call(trans.scope||window, null, trans.arg, false);
12372     }
12373 });/*
12374  * Based on:
12375  * Ext JS Library 1.1.1
12376  * Copyright(c) 2006-2007, Ext JS, LLC.
12377  *
12378  * Originally Released Under LGPL - original licence link has changed is not relivant.
12379  *
12380  * Fork - LGPL
12381  * <script type="text/javascript">
12382  */
12383
12384 /**
12385  * @class Roo.data.JsonReader
12386  * @extends Roo.data.DataReader
12387  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12388  * based on mappings in a provided Roo.data.Record constructor.
12389  * 
12390  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12391  * in the reply previously. 
12392  * 
12393  * <p>
12394  * Example code:
12395  * <pre><code>
12396 var RecordDef = Roo.data.Record.create([
12397     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12398     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12399 ]);
12400 var myReader = new Roo.data.JsonReader({
12401     totalProperty: "results",    // The property which contains the total dataset size (optional)
12402     root: "rows",                // The property which contains an Array of row objects
12403     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12404 }, RecordDef);
12405 </code></pre>
12406  * <p>
12407  * This would consume a JSON file like this:
12408  * <pre><code>
12409 { 'results': 2, 'rows': [
12410     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12411     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12412 }
12413 </code></pre>
12414  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12415  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12416  * paged from the remote server.
12417  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12418  * @cfg {String} root name of the property which contains the Array of row objects.
12419  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12420  * @cfg {Array} fields Array of field definition objects
12421  * @constructor
12422  * Create a new JsonReader
12423  * @param {Object} meta Metadata configuration options
12424  * @param {Object} recordType Either an Array of field definition objects,
12425  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12426  */
12427 Roo.data.JsonReader = function(meta, recordType){
12428     
12429     meta = meta || {};
12430     // set some defaults:
12431     Roo.applyIf(meta, {
12432         totalProperty: 'total',
12433         successProperty : 'success',
12434         root : 'data',
12435         id : 'id'
12436     });
12437     
12438     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12439 };
12440 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12441     
12442     /**
12443      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12444      * Used by Store query builder to append _requestMeta to params.
12445      * 
12446      */
12447     metaFromRemote : false,
12448     /**
12449      * This method is only used by a DataProxy which has retrieved data from a remote server.
12450      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12451      * @return {Object} data A data block which is used by an Roo.data.Store object as
12452      * a cache of Roo.data.Records.
12453      */
12454     read : function(response){
12455         var json = response.responseText;
12456        
12457         var o = /* eval:var:o */ eval("("+json+")");
12458         if(!o) {
12459             throw {message: "JsonReader.read: Json object not found"};
12460         }
12461         
12462         if(o.metaData){
12463             
12464             delete this.ef;
12465             this.metaFromRemote = true;
12466             this.meta = o.metaData;
12467             this.recordType = Roo.data.Record.create(o.metaData.fields);
12468             this.onMetaChange(this.meta, this.recordType, o);
12469         }
12470         return this.readRecords(o);
12471     },
12472
12473     // private function a store will implement
12474     onMetaChange : function(meta, recordType, o){
12475
12476     },
12477
12478     /**
12479          * @ignore
12480          */
12481     simpleAccess: function(obj, subsc) {
12482         return obj[subsc];
12483     },
12484
12485         /**
12486          * @ignore
12487          */
12488     getJsonAccessor: function(){
12489         var re = /[\[\.]/;
12490         return function(expr) {
12491             try {
12492                 return(re.test(expr))
12493                     ? new Function("obj", "return obj." + expr)
12494                     : function(obj){
12495                         return obj[expr];
12496                     };
12497             } catch(e){}
12498             return Roo.emptyFn;
12499         };
12500     }(),
12501
12502     /**
12503      * Create a data block containing Roo.data.Records from an XML document.
12504      * @param {Object} o An object which contains an Array of row objects in the property specified
12505      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12506      * which contains the total size of the dataset.
12507      * @return {Object} data A data block which is used by an Roo.data.Store object as
12508      * a cache of Roo.data.Records.
12509      */
12510     readRecords : function(o){
12511         /**
12512          * After any data loads, the raw JSON data is available for further custom processing.
12513          * @type Object
12514          */
12515         this.o = o;
12516         var s = this.meta, Record = this.recordType,
12517             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12518
12519 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12520         if (!this.ef) {
12521             if(s.totalProperty) {
12522                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12523                 }
12524                 if(s.successProperty) {
12525                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12526                 }
12527                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12528                 if (s.id) {
12529                         var g = this.getJsonAccessor(s.id);
12530                         this.getId = function(rec) {
12531                                 var r = g(rec);  
12532                                 return (r === undefined || r === "") ? null : r;
12533                         };
12534                 } else {
12535                         this.getId = function(){return null;};
12536                 }
12537             this.ef = [];
12538             for(var jj = 0; jj < fl; jj++){
12539                 f = fi[jj];
12540                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12541                 this.ef[jj] = this.getJsonAccessor(map);
12542             }
12543         }
12544
12545         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12546         if(s.totalProperty){
12547             var vt = parseInt(this.getTotal(o), 10);
12548             if(!isNaN(vt)){
12549                 totalRecords = vt;
12550             }
12551         }
12552         if(s.successProperty){
12553             var vs = this.getSuccess(o);
12554             if(vs === false || vs === 'false'){
12555                 success = false;
12556             }
12557         }
12558         var records = [];
12559         for(var i = 0; i < c; i++){
12560                 var n = root[i];
12561             var values = {};
12562             var id = this.getId(n);
12563             for(var j = 0; j < fl; j++){
12564                 f = fi[j];
12565             var v = this.ef[j](n);
12566             if (!f.convert) {
12567                 Roo.log('missing convert for ' + f.name);
12568                 Roo.log(f);
12569                 continue;
12570             }
12571             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12572             }
12573             var record = new Record(values, id);
12574             record.json = n;
12575             records[i] = record;
12576         }
12577         return {
12578             raw : o,
12579             success : success,
12580             records : records,
12581             totalRecords : totalRecords
12582         };
12583     }
12584 });/*
12585  * Based on:
12586  * Ext JS Library 1.1.1
12587  * Copyright(c) 2006-2007, Ext JS, LLC.
12588  *
12589  * Originally Released Under LGPL - original licence link has changed is not relivant.
12590  *
12591  * Fork - LGPL
12592  * <script type="text/javascript">
12593  */
12594
12595 /**
12596  * @class Roo.data.ArrayReader
12597  * @extends Roo.data.DataReader
12598  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12599  * Each element of that Array represents a row of data fields. The
12600  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12601  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12602  * <p>
12603  * Example code:.
12604  * <pre><code>
12605 var RecordDef = Roo.data.Record.create([
12606     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12607     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12608 ]);
12609 var myReader = new Roo.data.ArrayReader({
12610     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12611 }, RecordDef);
12612 </code></pre>
12613  * <p>
12614  * This would consume an Array like this:
12615  * <pre><code>
12616 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12617   </code></pre>
12618  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12619  * @constructor
12620  * Create a new JsonReader
12621  * @param {Object} meta Metadata configuration options.
12622  * @param {Object} recordType Either an Array of field definition objects
12623  * as specified to {@link Roo.data.Record#create},
12624  * or an {@link Roo.data.Record} object
12625  * created using {@link Roo.data.Record#create}.
12626  */
12627 Roo.data.ArrayReader = function(meta, recordType){
12628     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12629 };
12630
12631 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12632     /**
12633      * Create a data block containing Roo.data.Records from an XML document.
12634      * @param {Object} o An Array of row objects which represents the dataset.
12635      * @return {Object} data A data block which is used by an Roo.data.Store object as
12636      * a cache of Roo.data.Records.
12637      */
12638     readRecords : function(o){
12639         var sid = this.meta ? this.meta.id : null;
12640         var recordType = this.recordType, fields = recordType.prototype.fields;
12641         var records = [];
12642         var root = o;
12643             for(var i = 0; i < root.length; i++){
12644                     var n = root[i];
12645                 var values = {};
12646                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12647                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12648                 var f = fields.items[j];
12649                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12650                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12651                 v = f.convert(v);
12652                 values[f.name] = v;
12653             }
12654                 var record = new recordType(values, id);
12655                 record.json = n;
12656                 records[records.length] = record;
12657             }
12658             return {
12659                 records : records,
12660                 totalRecords : records.length
12661             };
12662     }
12663 });/*
12664  * - LGPL
12665  * * 
12666  */
12667
12668 /**
12669  * @class Roo.bootstrap.ComboBox
12670  * @extends Roo.bootstrap.TriggerField
12671  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12672  * @cfg {Boolean} append (true|false) default false
12673  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12674  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12675  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12676  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12677  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12678  * @cfg {Boolean} animate default true
12679  * @cfg {Boolean} emptyResultText only for touch device
12680  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12681  * @cfg {String} emptyTitle default ''
12682  * @constructor
12683  * Create a new ComboBox.
12684  * @param {Object} config Configuration options
12685  */
12686 Roo.bootstrap.ComboBox = function(config){
12687     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12688     this.addEvents({
12689         /**
12690          * @event expand
12691          * Fires when the dropdown list is expanded
12692         * @param {Roo.bootstrap.ComboBox} combo This combo box
12693         */
12694         'expand' : true,
12695         /**
12696          * @event collapse
12697          * Fires when the dropdown list is collapsed
12698         * @param {Roo.bootstrap.ComboBox} combo This combo box
12699         */
12700         'collapse' : true,
12701         /**
12702          * @event beforeselect
12703          * Fires before a list item is selected. Return false to cancel the selection.
12704         * @param {Roo.bootstrap.ComboBox} combo This combo box
12705         * @param {Roo.data.Record} record The data record returned from the underlying store
12706         * @param {Number} index The index of the selected item in the dropdown list
12707         */
12708         'beforeselect' : true,
12709         /**
12710          * @event select
12711          * Fires when a list item is selected
12712         * @param {Roo.bootstrap.ComboBox} combo This combo box
12713         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12714         * @param {Number} index The index of the selected item in the dropdown list
12715         */
12716         'select' : true,
12717         /**
12718          * @event beforequery
12719          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12720          * The event object passed has these properties:
12721         * @param {Roo.bootstrap.ComboBox} combo This combo box
12722         * @param {String} query The query
12723         * @param {Boolean} forceAll true to force "all" query
12724         * @param {Boolean} cancel true to cancel the query
12725         * @param {Object} e The query event object
12726         */
12727         'beforequery': true,
12728          /**
12729          * @event add
12730          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12731         * @param {Roo.bootstrap.ComboBox} combo This combo box
12732         */
12733         'add' : true,
12734         /**
12735          * @event edit
12736          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12737         * @param {Roo.bootstrap.ComboBox} combo This combo box
12738         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12739         */
12740         'edit' : true,
12741         /**
12742          * @event remove
12743          * Fires when the remove value from the combobox array
12744         * @param {Roo.bootstrap.ComboBox} combo This combo box
12745         */
12746         'remove' : true,
12747         /**
12748          * @event afterremove
12749          * Fires when the remove value from the combobox array
12750         * @param {Roo.bootstrap.ComboBox} combo This combo box
12751         */
12752         'afterremove' : true,
12753         /**
12754          * @event specialfilter
12755          * Fires when specialfilter
12756             * @param {Roo.bootstrap.ComboBox} combo This combo box
12757             */
12758         'specialfilter' : true,
12759         /**
12760          * @event tick
12761          * Fires when tick the element
12762             * @param {Roo.bootstrap.ComboBox} combo This combo box
12763             */
12764         'tick' : true,
12765         /**
12766          * @event touchviewdisplay
12767          * Fires when touch view require special display (default is using displayField)
12768             * @param {Roo.bootstrap.ComboBox} combo This combo box
12769             * @param {Object} cfg set html .
12770             */
12771         'touchviewdisplay' : true
12772         
12773     });
12774     
12775     this.item = [];
12776     this.tickItems = [];
12777     
12778     this.selectedIndex = -1;
12779     if(this.mode == 'local'){
12780         if(config.queryDelay === undefined){
12781             this.queryDelay = 10;
12782         }
12783         if(config.minChars === undefined){
12784             this.minChars = 0;
12785         }
12786     }
12787 };
12788
12789 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12790      
12791     /**
12792      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12793      * rendering into an Roo.Editor, defaults to false)
12794      */
12795     /**
12796      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12797      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12798      */
12799     /**
12800      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12801      */
12802     /**
12803      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12804      * the dropdown list (defaults to undefined, with no header element)
12805      */
12806
12807      /**
12808      * @cfg {String/Roo.Template} tpl The template to use to render the output
12809      */
12810      
12811      /**
12812      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12813      */
12814     listWidth: undefined,
12815     /**
12816      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12817      * mode = 'remote' or 'text' if mode = 'local')
12818      */
12819     displayField: undefined,
12820     
12821     /**
12822      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12823      * mode = 'remote' or 'value' if mode = 'local'). 
12824      * Note: use of a valueField requires the user make a selection
12825      * in order for a value to be mapped.
12826      */
12827     valueField: undefined,
12828     /**
12829      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12830      */
12831     modalTitle : '',
12832     
12833     /**
12834      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12835      * field's data value (defaults to the underlying DOM element's name)
12836      */
12837     hiddenName: undefined,
12838     /**
12839      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12840      */
12841     listClass: '',
12842     /**
12843      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12844      */
12845     selectedClass: 'active',
12846     
12847     /**
12848      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12849      */
12850     shadow:'sides',
12851     /**
12852      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12853      * anchor positions (defaults to 'tl-bl')
12854      */
12855     listAlign: 'tl-bl?',
12856     /**
12857      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12858      */
12859     maxHeight: 300,
12860     /**
12861      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12862      * query specified by the allQuery config option (defaults to 'query')
12863      */
12864     triggerAction: 'query',
12865     /**
12866      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12867      * (defaults to 4, does not apply if editable = false)
12868      */
12869     minChars : 4,
12870     /**
12871      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12872      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12873      */
12874     typeAhead: false,
12875     /**
12876      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12877      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12878      */
12879     queryDelay: 500,
12880     /**
12881      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12882      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12883      */
12884     pageSize: 0,
12885     /**
12886      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12887      * when editable = true (defaults to false)
12888      */
12889     selectOnFocus:false,
12890     /**
12891      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12892      */
12893     queryParam: 'query',
12894     /**
12895      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12896      * when mode = 'remote' (defaults to 'Loading...')
12897      */
12898     loadingText: 'Loading...',
12899     /**
12900      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12901      */
12902     resizable: false,
12903     /**
12904      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12905      */
12906     handleHeight : 8,
12907     /**
12908      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12909      * traditional select (defaults to true)
12910      */
12911     editable: true,
12912     /**
12913      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12914      */
12915     allQuery: '',
12916     /**
12917      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12918      */
12919     mode: 'remote',
12920     /**
12921      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12922      * listWidth has a higher value)
12923      */
12924     minListWidth : 70,
12925     /**
12926      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12927      * allow the user to set arbitrary text into the field (defaults to false)
12928      */
12929     forceSelection:false,
12930     /**
12931      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12932      * if typeAhead = true (defaults to 250)
12933      */
12934     typeAheadDelay : 250,
12935     /**
12936      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12937      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12938      */
12939     valueNotFoundText : undefined,
12940     /**
12941      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12942      */
12943     blockFocus : false,
12944     
12945     /**
12946      * @cfg {Boolean} disableClear Disable showing of clear button.
12947      */
12948     disableClear : false,
12949     /**
12950      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12951      */
12952     alwaysQuery : false,
12953     
12954     /**
12955      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12956      */
12957     multiple : false,
12958     
12959     /**
12960      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12961      */
12962     invalidClass : "has-warning",
12963     
12964     /**
12965      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12966      */
12967     validClass : "has-success",
12968     
12969     /**
12970      * @cfg {Boolean} specialFilter (true|false) special filter default false
12971      */
12972     specialFilter : false,
12973     
12974     /**
12975      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12976      */
12977     mobileTouchView : true,
12978     
12979     /**
12980      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12981      */
12982     useNativeIOS : false,
12983     
12984     ios_options : false,
12985     
12986     //private
12987     addicon : false,
12988     editicon: false,
12989     
12990     page: 0,
12991     hasQuery: false,
12992     append: false,
12993     loadNext: false,
12994     autoFocus : true,
12995     tickable : false,
12996     btnPosition : 'right',
12997     triggerList : true,
12998     showToggleBtn : true,
12999     animate : true,
13000     emptyResultText: 'Empty',
13001     triggerText : 'Select',
13002     emptyTitle : '',
13003     
13004     // element that contains real text value.. (when hidden is used..)
13005     
13006     getAutoCreate : function()
13007     {   
13008         var cfg = false;
13009         //render
13010         /*
13011          * Render classic select for iso
13012          */
13013         
13014         if(Roo.isIOS && this.useNativeIOS){
13015             cfg = this.getAutoCreateNativeIOS();
13016             return cfg;
13017         }
13018         
13019         /*
13020          * Touch Devices
13021          */
13022         
13023         if(Roo.isTouch && this.mobileTouchView){
13024             cfg = this.getAutoCreateTouchView();
13025             return cfg;;
13026         }
13027         
13028         /*
13029          *  Normal ComboBox
13030          */
13031         if(!this.tickable){
13032             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13033             return cfg;
13034         }
13035         
13036         /*
13037          *  ComboBox with tickable selections
13038          */
13039              
13040         var align = this.labelAlign || this.parentLabelAlign();
13041         
13042         cfg = {
13043             cls : 'form-group roo-combobox-tickable' //input-group
13044         };
13045         
13046         var btn_text_select = '';
13047         var btn_text_done = '';
13048         var btn_text_cancel = '';
13049         
13050         if (this.btn_text_show) {
13051             btn_text_select = 'Select';
13052             btn_text_done = 'Done';
13053             btn_text_cancel = 'Cancel'; 
13054         }
13055         
13056         var buttons = {
13057             tag : 'div',
13058             cls : 'tickable-buttons',
13059             cn : [
13060                 {
13061                     tag : 'button',
13062                     type : 'button',
13063                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13064                     //html : this.triggerText
13065                     html: btn_text_select
13066                 },
13067                 {
13068                     tag : 'button',
13069                     type : 'button',
13070                     name : 'ok',
13071                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13072                     //html : 'Done'
13073                     html: btn_text_done
13074                 },
13075                 {
13076                     tag : 'button',
13077                     type : 'button',
13078                     name : 'cancel',
13079                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13080                     //html : 'Cancel'
13081                     html: btn_text_cancel
13082                 }
13083             ]
13084         };
13085         
13086         if(this.editable){
13087             buttons.cn.unshift({
13088                 tag: 'input',
13089                 cls: 'roo-select2-search-field-input'
13090             });
13091         }
13092         
13093         var _this = this;
13094         
13095         Roo.each(buttons.cn, function(c){
13096             if (_this.size) {
13097                 c.cls += ' btn-' + _this.size;
13098             }
13099
13100             if (_this.disabled) {
13101                 c.disabled = true;
13102             }
13103         });
13104         
13105         var box = {
13106             tag: 'div',
13107             cn: [
13108                 {
13109                     tag: 'input',
13110                     type : 'hidden',
13111                     cls: 'form-hidden-field'
13112                 },
13113                 {
13114                     tag: 'ul',
13115                     cls: 'roo-select2-choices',
13116                     cn:[
13117                         {
13118                             tag: 'li',
13119                             cls: 'roo-select2-search-field',
13120                             cn: [
13121                                 buttons
13122                             ]
13123                         }
13124                     ]
13125                 }
13126             ]
13127         };
13128         
13129         var combobox = {
13130             cls: 'roo-select2-container input-group roo-select2-container-multi',
13131             cn: [
13132                 box
13133 //                {
13134 //                    tag: 'ul',
13135 //                    cls: 'typeahead typeahead-long dropdown-menu',
13136 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13137 //                }
13138             ]
13139         };
13140         
13141         if(this.hasFeedback && !this.allowBlank){
13142             
13143             var feedback = {
13144                 tag: 'span',
13145                 cls: 'glyphicon form-control-feedback'
13146             };
13147
13148             combobox.cn.push(feedback);
13149         }
13150         
13151         
13152         if (align ==='left' && this.fieldLabel.length) {
13153             
13154             cfg.cls += ' roo-form-group-label-left';
13155             
13156             cfg.cn = [
13157                 {
13158                     tag : 'i',
13159                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13160                     tooltip : 'This field is required'
13161                 },
13162                 {
13163                     tag: 'label',
13164                     'for' :  id,
13165                     cls : 'control-label',
13166                     html : this.fieldLabel
13167
13168                 },
13169                 {
13170                     cls : "", 
13171                     cn: [
13172                         combobox
13173                     ]
13174                 }
13175
13176             ];
13177             
13178             var labelCfg = cfg.cn[1];
13179             var contentCfg = cfg.cn[2];
13180             
13181
13182             if(this.indicatorpos == 'right'){
13183                 
13184                 cfg.cn = [
13185                     {
13186                         tag: 'label',
13187                         'for' :  id,
13188                         cls : 'control-label',
13189                         cn : [
13190                             {
13191                                 tag : 'span',
13192                                 html : this.fieldLabel
13193                             },
13194                             {
13195                                 tag : 'i',
13196                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13197                                 tooltip : 'This field is required'
13198                             }
13199                         ]
13200                     },
13201                     {
13202                         cls : "",
13203                         cn: [
13204                             combobox
13205                         ]
13206                     }
13207
13208                 ];
13209                 
13210                 
13211                 
13212                 labelCfg = cfg.cn[0];
13213                 contentCfg = cfg.cn[1];
13214             
13215             }
13216             
13217             if(this.labelWidth > 12){
13218                 labelCfg.style = "width: " + this.labelWidth + 'px';
13219             }
13220             
13221             if(this.labelWidth < 13 && this.labelmd == 0){
13222                 this.labelmd = this.labelWidth;
13223             }
13224             
13225             if(this.labellg > 0){
13226                 labelCfg.cls += ' col-lg-' + this.labellg;
13227                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13228             }
13229             
13230             if(this.labelmd > 0){
13231                 labelCfg.cls += ' col-md-' + this.labelmd;
13232                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13233             }
13234             
13235             if(this.labelsm > 0){
13236                 labelCfg.cls += ' col-sm-' + this.labelsm;
13237                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13238             }
13239             
13240             if(this.labelxs > 0){
13241                 labelCfg.cls += ' col-xs-' + this.labelxs;
13242                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13243             }
13244                 
13245                 
13246         } else if ( this.fieldLabel.length) {
13247 //                Roo.log(" label");
13248                  cfg.cn = [
13249                     {
13250                         tag : 'i',
13251                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13252                         tooltip : 'This field is required'
13253                     },
13254                     {
13255                         tag: 'label',
13256                         //cls : 'input-group-addon',
13257                         html : this.fieldLabel
13258                     },
13259                     combobox
13260                 ];
13261                 
13262                 if(this.indicatorpos == 'right'){
13263                     cfg.cn = [
13264                         {
13265                             tag: 'label',
13266                             //cls : 'input-group-addon',
13267                             html : this.fieldLabel
13268                         },
13269                         {
13270                             tag : 'i',
13271                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13272                             tooltip : 'This field is required'
13273                         },
13274                         combobox
13275                     ];
13276                     
13277                 }
13278
13279         } else {
13280             
13281 //                Roo.log(" no label && no align");
13282                 cfg = combobox
13283                      
13284                 
13285         }
13286          
13287         var settings=this;
13288         ['xs','sm','md','lg'].map(function(size){
13289             if (settings[size]) {
13290                 cfg.cls += ' col-' + size + '-' + settings[size];
13291             }
13292         });
13293         
13294         return cfg;
13295         
13296     },
13297     
13298     _initEventsCalled : false,
13299     
13300     // private
13301     initEvents: function()
13302     {   
13303         if (this._initEventsCalled) { // as we call render... prevent looping...
13304             return;
13305         }
13306         this._initEventsCalled = true;
13307         
13308         if (!this.store) {
13309             throw "can not find store for combo";
13310         }
13311         
13312         this.indicator = this.indicatorEl();
13313         
13314         this.store = Roo.factory(this.store, Roo.data);
13315         this.store.parent = this;
13316         
13317         // if we are building from html. then this element is so complex, that we can not really
13318         // use the rendered HTML.
13319         // so we have to trash and replace the previous code.
13320         if (Roo.XComponent.build_from_html) {
13321             // remove this element....
13322             var e = this.el.dom, k=0;
13323             while (e ) { e = e.previousSibling;  ++k;}
13324
13325             this.el.remove();
13326             
13327             this.el=false;
13328             this.rendered = false;
13329             
13330             this.render(this.parent().getChildContainer(true), k);
13331         }
13332         
13333         if(Roo.isIOS && this.useNativeIOS){
13334             this.initIOSView();
13335             return;
13336         }
13337         
13338         /*
13339          * Touch Devices
13340          */
13341         
13342         if(Roo.isTouch && this.mobileTouchView){
13343             this.initTouchView();
13344             return;
13345         }
13346         
13347         if(this.tickable){
13348             this.initTickableEvents();
13349             return;
13350         }
13351         
13352         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13353         
13354         if(this.hiddenName){
13355             
13356             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13357             
13358             this.hiddenField.dom.value =
13359                 this.hiddenValue !== undefined ? this.hiddenValue :
13360                 this.value !== undefined ? this.value : '';
13361
13362             // prevent input submission
13363             this.el.dom.removeAttribute('name');
13364             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13365              
13366              
13367         }
13368         //if(Roo.isGecko){
13369         //    this.el.dom.setAttribute('autocomplete', 'off');
13370         //}
13371         
13372         var cls = 'x-combo-list';
13373         
13374         //this.list = new Roo.Layer({
13375         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13376         //});
13377         
13378         var _this = this;
13379         
13380         (function(){
13381             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13382             _this.list.setWidth(lw);
13383         }).defer(100);
13384         
13385         this.list.on('mouseover', this.onViewOver, this);
13386         this.list.on('mousemove', this.onViewMove, this);
13387         this.list.on('scroll', this.onViewScroll, this);
13388         
13389         /*
13390         this.list.swallowEvent('mousewheel');
13391         this.assetHeight = 0;
13392
13393         if(this.title){
13394             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13395             this.assetHeight += this.header.getHeight();
13396         }
13397
13398         this.innerList = this.list.createChild({cls:cls+'-inner'});
13399         this.innerList.on('mouseover', this.onViewOver, this);
13400         this.innerList.on('mousemove', this.onViewMove, this);
13401         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13402         
13403         if(this.allowBlank && !this.pageSize && !this.disableClear){
13404             this.footer = this.list.createChild({cls:cls+'-ft'});
13405             this.pageTb = new Roo.Toolbar(this.footer);
13406            
13407         }
13408         if(this.pageSize){
13409             this.footer = this.list.createChild({cls:cls+'-ft'});
13410             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13411                     {pageSize: this.pageSize});
13412             
13413         }
13414         
13415         if (this.pageTb && this.allowBlank && !this.disableClear) {
13416             var _this = this;
13417             this.pageTb.add(new Roo.Toolbar.Fill(), {
13418                 cls: 'x-btn-icon x-btn-clear',
13419                 text: '&#160;',
13420                 handler: function()
13421                 {
13422                     _this.collapse();
13423                     _this.clearValue();
13424                     _this.onSelect(false, -1);
13425                 }
13426             });
13427         }
13428         if (this.footer) {
13429             this.assetHeight += this.footer.getHeight();
13430         }
13431         */
13432             
13433         if(!this.tpl){
13434             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13435         }
13436
13437         this.view = new Roo.View(this.list, this.tpl, {
13438             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13439         });
13440         //this.view.wrapEl.setDisplayed(false);
13441         this.view.on('click', this.onViewClick, this);
13442         
13443         
13444         this.store.on('beforeload', this.onBeforeLoad, this);
13445         this.store.on('load', this.onLoad, this);
13446         this.store.on('loadexception', this.onLoadException, this);
13447         /*
13448         if(this.resizable){
13449             this.resizer = new Roo.Resizable(this.list,  {
13450                pinned:true, handles:'se'
13451             });
13452             this.resizer.on('resize', function(r, w, h){
13453                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13454                 this.listWidth = w;
13455                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13456                 this.restrictHeight();
13457             }, this);
13458             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13459         }
13460         */
13461         if(!this.editable){
13462             this.editable = true;
13463             this.setEditable(false);
13464         }
13465         
13466         /*
13467         
13468         if (typeof(this.events.add.listeners) != 'undefined') {
13469             
13470             this.addicon = this.wrap.createChild(
13471                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13472        
13473             this.addicon.on('click', function(e) {
13474                 this.fireEvent('add', this);
13475             }, this);
13476         }
13477         if (typeof(this.events.edit.listeners) != 'undefined') {
13478             
13479             this.editicon = this.wrap.createChild(
13480                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13481             if (this.addicon) {
13482                 this.editicon.setStyle('margin-left', '40px');
13483             }
13484             this.editicon.on('click', function(e) {
13485                 
13486                 // we fire even  if inothing is selected..
13487                 this.fireEvent('edit', this, this.lastData );
13488                 
13489             }, this);
13490         }
13491         */
13492         
13493         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13494             "up" : function(e){
13495                 this.inKeyMode = true;
13496                 this.selectPrev();
13497             },
13498
13499             "down" : function(e){
13500                 if(!this.isExpanded()){
13501                     this.onTriggerClick();
13502                 }else{
13503                     this.inKeyMode = true;
13504                     this.selectNext();
13505                 }
13506             },
13507
13508             "enter" : function(e){
13509 //                this.onViewClick();
13510                 //return true;
13511                 this.collapse();
13512                 
13513                 if(this.fireEvent("specialkey", this, e)){
13514                     this.onViewClick(false);
13515                 }
13516                 
13517                 return true;
13518             },
13519
13520             "esc" : function(e){
13521                 this.collapse();
13522             },
13523
13524             "tab" : function(e){
13525                 this.collapse();
13526                 
13527                 if(this.fireEvent("specialkey", this, e)){
13528                     this.onViewClick(false);
13529                 }
13530                 
13531                 return true;
13532             },
13533
13534             scope : this,
13535
13536             doRelay : function(foo, bar, hname){
13537                 if(hname == 'down' || this.scope.isExpanded()){
13538                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13539                 }
13540                 return true;
13541             },
13542
13543             forceKeyDown: true
13544         });
13545         
13546         
13547         this.queryDelay = Math.max(this.queryDelay || 10,
13548                 this.mode == 'local' ? 10 : 250);
13549         
13550         
13551         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13552         
13553         if(this.typeAhead){
13554             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13555         }
13556         if(this.editable !== false){
13557             this.inputEl().on("keyup", this.onKeyUp, this);
13558         }
13559         if(this.forceSelection){
13560             this.inputEl().on('blur', this.doForce, this);
13561         }
13562         
13563         if(this.multiple){
13564             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13565             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13566         }
13567     },
13568     
13569     initTickableEvents: function()
13570     {   
13571         this.createList();
13572         
13573         if(this.hiddenName){
13574             
13575             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13576             
13577             this.hiddenField.dom.value =
13578                 this.hiddenValue !== undefined ? this.hiddenValue :
13579                 this.value !== undefined ? this.value : '';
13580
13581             // prevent input submission
13582             this.el.dom.removeAttribute('name');
13583             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13584              
13585              
13586         }
13587         
13588 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13589         
13590         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13591         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13592         if(this.triggerList){
13593             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13594         }
13595          
13596         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13597         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13598         
13599         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13600         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13601         
13602         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13603         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13604         
13605         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13606         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13607         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13608         
13609         this.okBtn.hide();
13610         this.cancelBtn.hide();
13611         
13612         var _this = this;
13613         
13614         (function(){
13615             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13616             _this.list.setWidth(lw);
13617         }).defer(100);
13618         
13619         this.list.on('mouseover', this.onViewOver, this);
13620         this.list.on('mousemove', this.onViewMove, this);
13621         
13622         this.list.on('scroll', this.onViewScroll, this);
13623         
13624         if(!this.tpl){
13625             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13626                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13627         }
13628
13629         this.view = new Roo.View(this.list, this.tpl, {
13630             singleSelect:true,
13631             tickable:true,
13632             parent:this,
13633             store: this.store,
13634             selectedClass: this.selectedClass
13635         });
13636         
13637         //this.view.wrapEl.setDisplayed(false);
13638         this.view.on('click', this.onViewClick, this);
13639         
13640         
13641         
13642         this.store.on('beforeload', this.onBeforeLoad, this);
13643         this.store.on('load', this.onLoad, this);
13644         this.store.on('loadexception', this.onLoadException, this);
13645         
13646         if(this.editable){
13647             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13648                 "up" : function(e){
13649                     this.inKeyMode = true;
13650                     this.selectPrev();
13651                 },
13652
13653                 "down" : function(e){
13654                     this.inKeyMode = true;
13655                     this.selectNext();
13656                 },
13657
13658                 "enter" : function(e){
13659                     if(this.fireEvent("specialkey", this, e)){
13660                         this.onViewClick(false);
13661                     }
13662                     
13663                     return true;
13664                 },
13665
13666                 "esc" : function(e){
13667                     this.onTickableFooterButtonClick(e, false, false);
13668                 },
13669
13670                 "tab" : function(e){
13671                     this.fireEvent("specialkey", this, e);
13672                     
13673                     this.onTickableFooterButtonClick(e, false, false);
13674                     
13675                     return true;
13676                 },
13677
13678                 scope : this,
13679
13680                 doRelay : function(e, fn, key){
13681                     if(this.scope.isExpanded()){
13682                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13683                     }
13684                     return true;
13685                 },
13686
13687                 forceKeyDown: true
13688             });
13689         }
13690         
13691         this.queryDelay = Math.max(this.queryDelay || 10,
13692                 this.mode == 'local' ? 10 : 250);
13693         
13694         
13695         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13696         
13697         if(this.typeAhead){
13698             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13699         }
13700         
13701         if(this.editable !== false){
13702             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13703         }
13704         
13705         this.indicator = this.indicatorEl();
13706         
13707         if(this.indicator){
13708             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13709             this.indicator.hide();
13710         }
13711         
13712     },
13713
13714     onDestroy : function(){
13715         if(this.view){
13716             this.view.setStore(null);
13717             this.view.el.removeAllListeners();
13718             this.view.el.remove();
13719             this.view.purgeListeners();
13720         }
13721         if(this.list){
13722             this.list.dom.innerHTML  = '';
13723         }
13724         
13725         if(this.store){
13726             this.store.un('beforeload', this.onBeforeLoad, this);
13727             this.store.un('load', this.onLoad, this);
13728             this.store.un('loadexception', this.onLoadException, this);
13729         }
13730         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13731     },
13732
13733     // private
13734     fireKey : function(e){
13735         if(e.isNavKeyPress() && !this.list.isVisible()){
13736             this.fireEvent("specialkey", this, e);
13737         }
13738     },
13739
13740     // private
13741     onResize: function(w, h){
13742 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13743 //        
13744 //        if(typeof w != 'number'){
13745 //            // we do not handle it!?!?
13746 //            return;
13747 //        }
13748 //        var tw = this.trigger.getWidth();
13749 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13750 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13751 //        var x = w - tw;
13752 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13753 //            
13754 //        //this.trigger.setStyle('left', x+'px');
13755 //        
13756 //        if(this.list && this.listWidth === undefined){
13757 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13758 //            this.list.setWidth(lw);
13759 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13760 //        }
13761         
13762     
13763         
13764     },
13765
13766     /**
13767      * Allow or prevent the user from directly editing the field text.  If false is passed,
13768      * the user will only be able to select from the items defined in the dropdown list.  This method
13769      * is the runtime equivalent of setting the 'editable' config option at config time.
13770      * @param {Boolean} value True to allow the user to directly edit the field text
13771      */
13772     setEditable : function(value){
13773         if(value == this.editable){
13774             return;
13775         }
13776         this.editable = value;
13777         if(!value){
13778             this.inputEl().dom.setAttribute('readOnly', true);
13779             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13780             this.inputEl().addClass('x-combo-noedit');
13781         }else{
13782             this.inputEl().dom.setAttribute('readOnly', false);
13783             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13784             this.inputEl().removeClass('x-combo-noedit');
13785         }
13786     },
13787
13788     // private
13789     
13790     onBeforeLoad : function(combo,opts){
13791         if(!this.hasFocus){
13792             return;
13793         }
13794          if (!opts.add) {
13795             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13796          }
13797         this.restrictHeight();
13798         this.selectedIndex = -1;
13799     },
13800
13801     // private
13802     onLoad : function(){
13803         
13804         this.hasQuery = false;
13805         
13806         if(!this.hasFocus){
13807             return;
13808         }
13809         
13810         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13811             this.loading.hide();
13812         }
13813         
13814         if(this.store.getCount() > 0){
13815             
13816             this.expand();
13817             this.restrictHeight();
13818             if(this.lastQuery == this.allQuery){
13819                 if(this.editable && !this.tickable){
13820                     this.inputEl().dom.select();
13821                 }
13822                 
13823                 if(
13824                     !this.selectByValue(this.value, true) &&
13825                     this.autoFocus && 
13826                     (
13827                         !this.store.lastOptions ||
13828                         typeof(this.store.lastOptions.add) == 'undefined' || 
13829                         this.store.lastOptions.add != true
13830                     )
13831                 ){
13832                     this.select(0, true);
13833                 }
13834             }else{
13835                 if(this.autoFocus){
13836                     this.selectNext();
13837                 }
13838                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13839                     this.taTask.delay(this.typeAheadDelay);
13840                 }
13841             }
13842         }else{
13843             this.onEmptyResults();
13844         }
13845         
13846         //this.el.focus();
13847     },
13848     // private
13849     onLoadException : function()
13850     {
13851         this.hasQuery = false;
13852         
13853         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13854             this.loading.hide();
13855         }
13856         
13857         if(this.tickable && this.editable){
13858             return;
13859         }
13860         
13861         this.collapse();
13862         // only causes errors at present
13863         //Roo.log(this.store.reader.jsonData);
13864         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13865             // fixme
13866             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13867         //}
13868         
13869         
13870     },
13871     // private
13872     onTypeAhead : function(){
13873         if(this.store.getCount() > 0){
13874             var r = this.store.getAt(0);
13875             var newValue = r.data[this.displayField];
13876             var len = newValue.length;
13877             var selStart = this.getRawValue().length;
13878             
13879             if(selStart != len){
13880                 this.setRawValue(newValue);
13881                 this.selectText(selStart, newValue.length);
13882             }
13883         }
13884     },
13885
13886     // private
13887     onSelect : function(record, index){
13888         
13889         if(this.fireEvent('beforeselect', this, record, index) !== false){
13890         
13891             this.setFromData(index > -1 ? record.data : false);
13892             
13893             this.collapse();
13894             this.fireEvent('select', this, record, index);
13895         }
13896     },
13897
13898     /**
13899      * Returns the currently selected field value or empty string if no value is set.
13900      * @return {String} value The selected value
13901      */
13902     getValue : function()
13903     {
13904         if(Roo.isIOS && this.useNativeIOS){
13905             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13906         }
13907         
13908         if(this.multiple){
13909             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13910         }
13911         
13912         if(this.valueField){
13913             return typeof this.value != 'undefined' ? this.value : '';
13914         }else{
13915             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13916         }
13917     },
13918     
13919     getRawValue : function()
13920     {
13921         if(Roo.isIOS && this.useNativeIOS){
13922             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13923         }
13924         
13925         var v = this.inputEl().getValue();
13926         
13927         return v;
13928     },
13929
13930     /**
13931      * Clears any text/value currently set in the field
13932      */
13933     clearValue : function(){
13934         
13935         if(this.hiddenField){
13936             this.hiddenField.dom.value = '';
13937         }
13938         this.value = '';
13939         this.setRawValue('');
13940         this.lastSelectionText = '';
13941         this.lastData = false;
13942         
13943         var close = this.closeTriggerEl();
13944         
13945         if(close){
13946             close.hide();
13947         }
13948         
13949         this.validate();
13950         
13951     },
13952
13953     /**
13954      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13955      * will be displayed in the field.  If the value does not match the data value of an existing item,
13956      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13957      * Otherwise the field will be blank (although the value will still be set).
13958      * @param {String} value The value to match
13959      */
13960     setValue : function(v)
13961     {
13962         if(Roo.isIOS && this.useNativeIOS){
13963             this.setIOSValue(v);
13964             return;
13965         }
13966         
13967         if(this.multiple){
13968             this.syncValue();
13969             return;
13970         }
13971         
13972         var text = v;
13973         if(this.valueField){
13974             var r = this.findRecord(this.valueField, v);
13975             if(r){
13976                 text = r.data[this.displayField];
13977             }else if(this.valueNotFoundText !== undefined){
13978                 text = this.valueNotFoundText;
13979             }
13980         }
13981         this.lastSelectionText = text;
13982         if(this.hiddenField){
13983             this.hiddenField.dom.value = v;
13984         }
13985         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13986         this.value = v;
13987         
13988         var close = this.closeTriggerEl();
13989         
13990         if(close){
13991             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13992         }
13993         
13994         this.validate();
13995     },
13996     /**
13997      * @property {Object} the last set data for the element
13998      */
13999     
14000     lastData : false,
14001     /**
14002      * Sets the value of the field based on a object which is related to the record format for the store.
14003      * @param {Object} value the value to set as. or false on reset?
14004      */
14005     setFromData : function(o){
14006         
14007         if(this.multiple){
14008             this.addItem(o);
14009             return;
14010         }
14011             
14012         var dv = ''; // display value
14013         var vv = ''; // value value..
14014         this.lastData = o;
14015         if (this.displayField) {
14016             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14017         } else {
14018             // this is an error condition!!!
14019             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14020         }
14021         
14022         if(this.valueField){
14023             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14024         }
14025         
14026         var close = this.closeTriggerEl();
14027         
14028         if(close){
14029             if(dv.length || vv * 1 > 0){
14030                 close.show() ;
14031                 this.blockFocus=true;
14032             } else {
14033                 close.hide();
14034             }             
14035         }
14036         
14037         if(this.hiddenField){
14038             this.hiddenField.dom.value = vv;
14039             
14040             this.lastSelectionText = dv;
14041             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14042             this.value = vv;
14043             return;
14044         }
14045         // no hidden field.. - we store the value in 'value', but still display
14046         // display field!!!!
14047         this.lastSelectionText = dv;
14048         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14049         this.value = vv;
14050         
14051         
14052         
14053     },
14054     // private
14055     reset : function(){
14056         // overridden so that last data is reset..
14057         
14058         if(this.multiple){
14059             this.clearItem();
14060             return;
14061         }
14062         
14063         this.setValue(this.originalValue);
14064         //this.clearInvalid();
14065         this.lastData = false;
14066         if (this.view) {
14067             this.view.clearSelections();
14068         }
14069         
14070         this.validate();
14071     },
14072     // private
14073     findRecord : function(prop, value){
14074         var record;
14075         if(this.store.getCount() > 0){
14076             this.store.each(function(r){
14077                 if(r.data[prop] == value){
14078                     record = r;
14079                     return false;
14080                 }
14081                 return true;
14082             });
14083         }
14084         return record;
14085     },
14086     
14087     getName: function()
14088     {
14089         // returns hidden if it's set..
14090         if (!this.rendered) {return ''};
14091         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14092         
14093     },
14094     // private
14095     onViewMove : function(e, t){
14096         this.inKeyMode = false;
14097     },
14098
14099     // private
14100     onViewOver : function(e, t){
14101         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14102             return;
14103         }
14104         var item = this.view.findItemFromChild(t);
14105         
14106         if(item){
14107             var index = this.view.indexOf(item);
14108             this.select(index, false);
14109         }
14110     },
14111
14112     // private
14113     onViewClick : function(view, doFocus, el, e)
14114     {
14115         var index = this.view.getSelectedIndexes()[0];
14116         
14117         var r = this.store.getAt(index);
14118         
14119         if(this.tickable){
14120             
14121             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14122                 return;
14123             }
14124             
14125             var rm = false;
14126             var _this = this;
14127             
14128             Roo.each(this.tickItems, function(v,k){
14129                 
14130                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14131                     Roo.log(v);
14132                     _this.tickItems.splice(k, 1);
14133                     
14134                     if(typeof(e) == 'undefined' && view == false){
14135                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14136                     }
14137                     
14138                     rm = true;
14139                     return;
14140                 }
14141             });
14142             
14143             if(rm){
14144                 return;
14145             }
14146             
14147             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14148                 this.tickItems.push(r.data);
14149             }
14150             
14151             if(typeof(e) == 'undefined' && view == false){
14152                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14153             }
14154                     
14155             return;
14156         }
14157         
14158         if(r){
14159             this.onSelect(r, index);
14160         }
14161         if(doFocus !== false && !this.blockFocus){
14162             this.inputEl().focus();
14163         }
14164     },
14165
14166     // private
14167     restrictHeight : function(){
14168         //this.innerList.dom.style.height = '';
14169         //var inner = this.innerList.dom;
14170         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14171         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14172         //this.list.beginUpdate();
14173         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14174         this.list.alignTo(this.inputEl(), this.listAlign);
14175         this.list.alignTo(this.inputEl(), this.listAlign);
14176         //this.list.endUpdate();
14177     },
14178
14179     // private
14180     onEmptyResults : function(){
14181         
14182         if(this.tickable && this.editable){
14183             this.hasFocus = false;
14184             this.restrictHeight();
14185             return;
14186         }
14187         
14188         this.collapse();
14189     },
14190
14191     /**
14192      * Returns true if the dropdown list is expanded, else false.
14193      */
14194     isExpanded : function(){
14195         return this.list.isVisible();
14196     },
14197
14198     /**
14199      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14200      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14201      * @param {String} value The data value of the item to select
14202      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14203      * selected item if it is not currently in view (defaults to true)
14204      * @return {Boolean} True if the value matched an item in the list, else false
14205      */
14206     selectByValue : function(v, scrollIntoView){
14207         if(v !== undefined && v !== null){
14208             var r = this.findRecord(this.valueField || this.displayField, v);
14209             if(r){
14210                 this.select(this.store.indexOf(r), scrollIntoView);
14211                 return true;
14212             }
14213         }
14214         return false;
14215     },
14216
14217     /**
14218      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14219      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14220      * @param {Number} index The zero-based index of the list item to select
14221      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14222      * selected item if it is not currently in view (defaults to true)
14223      */
14224     select : function(index, scrollIntoView){
14225         this.selectedIndex = index;
14226         this.view.select(index);
14227         if(scrollIntoView !== false){
14228             var el = this.view.getNode(index);
14229             /*
14230              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14231              */
14232             if(el){
14233                 this.list.scrollChildIntoView(el, false);
14234             }
14235         }
14236     },
14237
14238     // private
14239     selectNext : 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 < ct-1){
14245                 this.select(this.selectedIndex+1);
14246             }
14247         }
14248     },
14249
14250     // private
14251     selectPrev : function(){
14252         var ct = this.store.getCount();
14253         if(ct > 0){
14254             if(this.selectedIndex == -1){
14255                 this.select(0);
14256             }else if(this.selectedIndex != 0){
14257                 this.select(this.selectedIndex-1);
14258             }
14259         }
14260     },
14261
14262     // private
14263     onKeyUp : function(e){
14264         if(this.editable !== false && !e.isSpecialKey()){
14265             this.lastKey = e.getKey();
14266             this.dqTask.delay(this.queryDelay);
14267         }
14268     },
14269
14270     // private
14271     validateBlur : function(){
14272         return !this.list || !this.list.isVisible();   
14273     },
14274
14275     // private
14276     initQuery : function(){
14277         
14278         var v = this.getRawValue();
14279         
14280         if(this.tickable && this.editable){
14281             v = this.tickableInputEl().getValue();
14282         }
14283         
14284         this.doQuery(v);
14285     },
14286
14287     // private
14288     doForce : function(){
14289         if(this.inputEl().dom.value.length > 0){
14290             this.inputEl().dom.value =
14291                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14292              
14293         }
14294     },
14295
14296     /**
14297      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14298      * query allowing the query action to be canceled if needed.
14299      * @param {String} query The SQL query to execute
14300      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14301      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14302      * saved in the current store (defaults to false)
14303      */
14304     doQuery : function(q, forceAll){
14305         
14306         if(q === undefined || q === null){
14307             q = '';
14308         }
14309         var qe = {
14310             query: q,
14311             forceAll: forceAll,
14312             combo: this,
14313             cancel:false
14314         };
14315         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14316             return false;
14317         }
14318         q = qe.query;
14319         
14320         forceAll = qe.forceAll;
14321         if(forceAll === true || (q.length >= this.minChars)){
14322             
14323             this.hasQuery = true;
14324             
14325             if(this.lastQuery != q || this.alwaysQuery){
14326                 this.lastQuery = q;
14327                 if(this.mode == 'local'){
14328                     this.selectedIndex = -1;
14329                     if(forceAll){
14330                         this.store.clearFilter();
14331                     }else{
14332                         
14333                         if(this.specialFilter){
14334                             this.fireEvent('specialfilter', this);
14335                             this.onLoad();
14336                             return;
14337                         }
14338                         
14339                         this.store.filter(this.displayField, q);
14340                     }
14341                     
14342                     this.store.fireEvent("datachanged", this.store);
14343                     
14344                     this.onLoad();
14345                     
14346                     
14347                 }else{
14348                     
14349                     this.store.baseParams[this.queryParam] = q;
14350                     
14351                     var options = {params : this.getParams(q)};
14352                     
14353                     if(this.loadNext){
14354                         options.add = true;
14355                         options.params.start = this.page * this.pageSize;
14356                     }
14357                     
14358                     this.store.load(options);
14359                     
14360                     /*
14361                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14362                      *  we should expand the list on onLoad
14363                      *  so command out it
14364                      */
14365 //                    this.expand();
14366                 }
14367             }else{
14368                 this.selectedIndex = -1;
14369                 this.onLoad();   
14370             }
14371         }
14372         
14373         this.loadNext = false;
14374     },
14375     
14376     // private
14377     getParams : function(q){
14378         var p = {};
14379         //p[this.queryParam] = q;
14380         
14381         if(this.pageSize){
14382             p.start = 0;
14383             p.limit = this.pageSize;
14384         }
14385         return p;
14386     },
14387
14388     /**
14389      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14390      */
14391     collapse : function(){
14392         if(!this.isExpanded()){
14393             return;
14394         }
14395         
14396         this.list.hide();
14397         
14398         this.hasFocus = false;
14399         
14400         if(this.tickable){
14401             this.okBtn.hide();
14402             this.cancelBtn.hide();
14403             this.trigger.show();
14404             
14405             if(this.editable){
14406                 this.tickableInputEl().dom.value = '';
14407                 this.tickableInputEl().blur();
14408             }
14409             
14410         }
14411         
14412         Roo.get(document).un('mousedown', this.collapseIf, this);
14413         Roo.get(document).un('mousewheel', this.collapseIf, this);
14414         if (!this.editable) {
14415             Roo.get(document).un('keydown', this.listKeyPress, this);
14416         }
14417         this.fireEvent('collapse', this);
14418         
14419         this.validate();
14420     },
14421
14422     // private
14423     collapseIf : function(e){
14424         var in_combo  = e.within(this.el);
14425         var in_list =  e.within(this.list);
14426         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14427         
14428         if (in_combo || in_list || is_list) {
14429             //e.stopPropagation();
14430             return;
14431         }
14432         
14433         if(this.tickable){
14434             this.onTickableFooterButtonClick(e, false, false);
14435         }
14436
14437         this.collapse();
14438         
14439     },
14440
14441     /**
14442      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14443      */
14444     expand : function(){
14445        
14446         if(this.isExpanded() || !this.hasFocus){
14447             return;
14448         }
14449         
14450         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14451         this.list.setWidth(lw);
14452         
14453         Roo.log('expand');
14454         
14455         this.list.show();
14456         
14457         this.restrictHeight();
14458         
14459         if(this.tickable){
14460             
14461             this.tickItems = Roo.apply([], this.item);
14462             
14463             this.okBtn.show();
14464             this.cancelBtn.show();
14465             this.trigger.hide();
14466             
14467             if(this.editable){
14468                 this.tickableInputEl().focus();
14469             }
14470             
14471         }
14472         
14473         Roo.get(document).on('mousedown', this.collapseIf, this);
14474         Roo.get(document).on('mousewheel', this.collapseIf, this);
14475         if (!this.editable) {
14476             Roo.get(document).on('keydown', this.listKeyPress, this);
14477         }
14478         
14479         this.fireEvent('expand', this);
14480     },
14481
14482     // private
14483     // Implements the default empty TriggerField.onTriggerClick function
14484     onTriggerClick : function(e)
14485     {
14486         Roo.log('trigger click');
14487         
14488         if(this.disabled || !this.triggerList){
14489             return;
14490         }
14491         
14492         this.page = 0;
14493         this.loadNext = false;
14494         
14495         if(this.isExpanded()){
14496             this.collapse();
14497             if (!this.blockFocus) {
14498                 this.inputEl().focus();
14499             }
14500             
14501         }else {
14502             this.hasFocus = true;
14503             if(this.triggerAction == 'all') {
14504                 this.doQuery(this.allQuery, true);
14505             } else {
14506                 this.doQuery(this.getRawValue());
14507             }
14508             if (!this.blockFocus) {
14509                 this.inputEl().focus();
14510             }
14511         }
14512     },
14513     
14514     onTickableTriggerClick : function(e)
14515     {
14516         if(this.disabled){
14517             return;
14518         }
14519         
14520         this.page = 0;
14521         this.loadNext = false;
14522         this.hasFocus = true;
14523         
14524         if(this.triggerAction == 'all') {
14525             this.doQuery(this.allQuery, true);
14526         } else {
14527             this.doQuery(this.getRawValue());
14528         }
14529     },
14530     
14531     onSearchFieldClick : function(e)
14532     {
14533         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14534             this.onTickableFooterButtonClick(e, false, false);
14535             return;
14536         }
14537         
14538         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14539             return;
14540         }
14541         
14542         this.page = 0;
14543         this.loadNext = false;
14544         this.hasFocus = true;
14545         
14546         if(this.triggerAction == 'all') {
14547             this.doQuery(this.allQuery, true);
14548         } else {
14549             this.doQuery(this.getRawValue());
14550         }
14551     },
14552     
14553     listKeyPress : function(e)
14554     {
14555         //Roo.log('listkeypress');
14556         // scroll to first matching element based on key pres..
14557         if (e.isSpecialKey()) {
14558             return false;
14559         }
14560         var k = String.fromCharCode(e.getKey()).toUpperCase();
14561         //Roo.log(k);
14562         var match  = false;
14563         var csel = this.view.getSelectedNodes();
14564         var cselitem = false;
14565         if (csel.length) {
14566             var ix = this.view.indexOf(csel[0]);
14567             cselitem  = this.store.getAt(ix);
14568             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14569                 cselitem = false;
14570             }
14571             
14572         }
14573         
14574         this.store.each(function(v) { 
14575             if (cselitem) {
14576                 // start at existing selection.
14577                 if (cselitem.id == v.id) {
14578                     cselitem = false;
14579                 }
14580                 return true;
14581             }
14582                 
14583             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14584                 match = this.store.indexOf(v);
14585                 return false;
14586             }
14587             return true;
14588         }, this);
14589         
14590         if (match === false) {
14591             return true; // no more action?
14592         }
14593         // scroll to?
14594         this.view.select(match);
14595         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14596         sn.scrollIntoView(sn.dom.parentNode, false);
14597     },
14598     
14599     onViewScroll : function(e, t){
14600         
14601         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){
14602             return;
14603         }
14604         
14605         this.hasQuery = true;
14606         
14607         this.loading = this.list.select('.loading', true).first();
14608         
14609         if(this.loading === null){
14610             this.list.createChild({
14611                 tag: 'div',
14612                 cls: 'loading roo-select2-more-results roo-select2-active',
14613                 html: 'Loading more results...'
14614             });
14615             
14616             this.loading = this.list.select('.loading', true).first();
14617             
14618             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14619             
14620             this.loading.hide();
14621         }
14622         
14623         this.loading.show();
14624         
14625         var _combo = this;
14626         
14627         this.page++;
14628         this.loadNext = true;
14629         
14630         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14631         
14632         return;
14633     },
14634     
14635     addItem : function(o)
14636     {   
14637         var dv = ''; // display value
14638         
14639         if (this.displayField) {
14640             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14641         } else {
14642             // this is an error condition!!!
14643             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14644         }
14645         
14646         if(!dv.length){
14647             return;
14648         }
14649         
14650         var choice = this.choices.createChild({
14651             tag: 'li',
14652             cls: 'roo-select2-search-choice',
14653             cn: [
14654                 {
14655                     tag: 'div',
14656                     html: dv
14657                 },
14658                 {
14659                     tag: 'a',
14660                     href: '#',
14661                     cls: 'roo-select2-search-choice-close fa fa-times',
14662                     tabindex: '-1'
14663                 }
14664             ]
14665             
14666         }, this.searchField);
14667         
14668         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14669         
14670         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14671         
14672         this.item.push(o);
14673         
14674         this.lastData = o;
14675         
14676         this.syncValue();
14677         
14678         this.inputEl().dom.value = '';
14679         
14680         this.validate();
14681     },
14682     
14683     onRemoveItem : function(e, _self, o)
14684     {
14685         e.preventDefault();
14686         
14687         this.lastItem = Roo.apply([], this.item);
14688         
14689         var index = this.item.indexOf(o.data) * 1;
14690         
14691         if( index < 0){
14692             Roo.log('not this item?!');
14693             return;
14694         }
14695         
14696         this.item.splice(index, 1);
14697         o.item.remove();
14698         
14699         this.syncValue();
14700         
14701         this.fireEvent('remove', this, e);
14702         
14703         this.validate();
14704         
14705     },
14706     
14707     syncValue : function()
14708     {
14709         if(!this.item.length){
14710             this.clearValue();
14711             return;
14712         }
14713             
14714         var value = [];
14715         var _this = this;
14716         Roo.each(this.item, function(i){
14717             if(_this.valueField){
14718                 value.push(i[_this.valueField]);
14719                 return;
14720             }
14721
14722             value.push(i);
14723         });
14724
14725         this.value = value.join(',');
14726
14727         if(this.hiddenField){
14728             this.hiddenField.dom.value = this.value;
14729         }
14730         
14731         this.store.fireEvent("datachanged", this.store);
14732         
14733         this.validate();
14734     },
14735     
14736     clearItem : function()
14737     {
14738         if(!this.multiple){
14739             return;
14740         }
14741         
14742         this.item = [];
14743         
14744         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14745            c.remove();
14746         });
14747         
14748         this.syncValue();
14749         
14750         this.validate();
14751         
14752         if(this.tickable && !Roo.isTouch){
14753             this.view.refresh();
14754         }
14755     },
14756     
14757     inputEl: function ()
14758     {
14759         if(Roo.isIOS && this.useNativeIOS){
14760             return this.el.select('select.roo-ios-select', true).first();
14761         }
14762         
14763         if(Roo.isTouch && this.mobileTouchView){
14764             return this.el.select('input.form-control',true).first();
14765         }
14766         
14767         if(this.tickable){
14768             return this.searchField;
14769         }
14770         
14771         return this.el.select('input.form-control',true).first();
14772     },
14773     
14774     onTickableFooterButtonClick : function(e, btn, el)
14775     {
14776         e.preventDefault();
14777         
14778         this.lastItem = Roo.apply([], this.item);
14779         
14780         if(btn && btn.name == 'cancel'){
14781             this.tickItems = Roo.apply([], this.item);
14782             this.collapse();
14783             return;
14784         }
14785         
14786         this.clearItem();
14787         
14788         var _this = this;
14789         
14790         Roo.each(this.tickItems, function(o){
14791             _this.addItem(o);
14792         });
14793         
14794         this.collapse();
14795         
14796     },
14797     
14798     validate : function()
14799     {
14800         if(this.getVisibilityEl().hasClass('hidden')){
14801             return true;
14802         }
14803         
14804         var v = this.getRawValue();
14805         
14806         if(this.multiple){
14807             v = this.getValue();
14808         }
14809         
14810         if(this.disabled || this.allowBlank || v.length){
14811             this.markValid();
14812             return true;
14813         }
14814         
14815         this.markInvalid();
14816         return false;
14817     },
14818     
14819     tickableInputEl : function()
14820     {
14821         if(!this.tickable || !this.editable){
14822             return this.inputEl();
14823         }
14824         
14825         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14826     },
14827     
14828     
14829     getAutoCreateTouchView : function()
14830     {
14831         var id = Roo.id();
14832         
14833         var cfg = {
14834             cls: 'form-group' //input-group
14835         };
14836         
14837         var input =  {
14838             tag: 'input',
14839             id : id,
14840             type : this.inputType,
14841             cls : 'form-control x-combo-noedit',
14842             autocomplete: 'new-password',
14843             placeholder : this.placeholder || '',
14844             readonly : true
14845         };
14846         
14847         if (this.name) {
14848             input.name = this.name;
14849         }
14850         
14851         if (this.size) {
14852             input.cls += ' input-' + this.size;
14853         }
14854         
14855         if (this.disabled) {
14856             input.disabled = true;
14857         }
14858         
14859         var inputblock = {
14860             cls : '',
14861             cn : [
14862                 input
14863             ]
14864         };
14865         
14866         if(this.before){
14867             inputblock.cls += ' input-group';
14868             
14869             inputblock.cn.unshift({
14870                 tag :'span',
14871                 cls : 'input-group-addon',
14872                 html : this.before
14873             });
14874         }
14875         
14876         if(this.removable && !this.multiple){
14877             inputblock.cls += ' roo-removable';
14878             
14879             inputblock.cn.push({
14880                 tag: 'button',
14881                 html : 'x',
14882                 cls : 'roo-combo-removable-btn close'
14883             });
14884         }
14885
14886         if(this.hasFeedback && !this.allowBlank){
14887             
14888             inputblock.cls += ' has-feedback';
14889             
14890             inputblock.cn.push({
14891                 tag: 'span',
14892                 cls: 'glyphicon form-control-feedback'
14893             });
14894             
14895         }
14896         
14897         if (this.after) {
14898             
14899             inputblock.cls += (this.before) ? '' : ' input-group';
14900             
14901             inputblock.cn.push({
14902                 tag :'span',
14903                 cls : 'input-group-addon',
14904                 html : this.after
14905             });
14906         }
14907
14908         var box = {
14909             tag: 'div',
14910             cn: [
14911                 {
14912                     tag: 'input',
14913                     type : 'hidden',
14914                     cls: 'form-hidden-field'
14915                 },
14916                 inputblock
14917             ]
14918             
14919         };
14920         
14921         if(this.multiple){
14922             box = {
14923                 tag: 'div',
14924                 cn: [
14925                     {
14926                         tag: 'input',
14927                         type : 'hidden',
14928                         cls: 'form-hidden-field'
14929                     },
14930                     {
14931                         tag: 'ul',
14932                         cls: 'roo-select2-choices',
14933                         cn:[
14934                             {
14935                                 tag: 'li',
14936                                 cls: 'roo-select2-search-field',
14937                                 cn: [
14938
14939                                     inputblock
14940                                 ]
14941                             }
14942                         ]
14943                     }
14944                 ]
14945             }
14946         };
14947         
14948         var combobox = {
14949             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14950             cn: [
14951                 box
14952             ]
14953         };
14954         
14955         if(!this.multiple && this.showToggleBtn){
14956             
14957             var caret = {
14958                         tag: 'span',
14959                         cls: 'caret'
14960             };
14961             
14962             if (this.caret != false) {
14963                 caret = {
14964                      tag: 'i',
14965                      cls: 'fa fa-' + this.caret
14966                 };
14967                 
14968             }
14969             
14970             combobox.cn.push({
14971                 tag :'span',
14972                 cls : 'input-group-addon btn dropdown-toggle',
14973                 cn : [
14974                     caret,
14975                     {
14976                         tag: 'span',
14977                         cls: 'combobox-clear',
14978                         cn  : [
14979                             {
14980                                 tag : 'i',
14981                                 cls: 'icon-remove'
14982                             }
14983                         ]
14984                     }
14985                 ]
14986
14987             })
14988         }
14989         
14990         if(this.multiple){
14991             combobox.cls += ' roo-select2-container-multi';
14992         }
14993         
14994         var align = this.labelAlign || this.parentLabelAlign();
14995         
14996         if (align ==='left' && this.fieldLabel.length) {
14997
14998             cfg.cn = [
14999                 {
15000                    tag : 'i',
15001                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15002                    tooltip : 'This field is required'
15003                 },
15004                 {
15005                     tag: 'label',
15006                     cls : 'control-label',
15007                     html : this.fieldLabel
15008
15009                 },
15010                 {
15011                     cls : '', 
15012                     cn: [
15013                         combobox
15014                     ]
15015                 }
15016             ];
15017             
15018             var labelCfg = cfg.cn[1];
15019             var contentCfg = cfg.cn[2];
15020             
15021
15022             if(this.indicatorpos == 'right'){
15023                 cfg.cn = [
15024                     {
15025                         tag: 'label',
15026                         'for' :  id,
15027                         cls : 'control-label',
15028                         cn : [
15029                             {
15030                                 tag : 'span',
15031                                 html : this.fieldLabel
15032                             },
15033                             {
15034                                 tag : 'i',
15035                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15036                                 tooltip : 'This field is required'
15037                             }
15038                         ]
15039                     },
15040                     {
15041                         cls : "",
15042                         cn: [
15043                             combobox
15044                         ]
15045                     }
15046
15047                 ];
15048                 
15049                 labelCfg = cfg.cn[0];
15050                 contentCfg = cfg.cn[1];
15051             }
15052             
15053            
15054             
15055             if(this.labelWidth > 12){
15056                 labelCfg.style = "width: " + this.labelWidth + 'px';
15057             }
15058             
15059             if(this.labelWidth < 13 && this.labelmd == 0){
15060                 this.labelmd = this.labelWidth;
15061             }
15062             
15063             if(this.labellg > 0){
15064                 labelCfg.cls += ' col-lg-' + this.labellg;
15065                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15066             }
15067             
15068             if(this.labelmd > 0){
15069                 labelCfg.cls += ' col-md-' + this.labelmd;
15070                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15071             }
15072             
15073             if(this.labelsm > 0){
15074                 labelCfg.cls += ' col-sm-' + this.labelsm;
15075                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15076             }
15077             
15078             if(this.labelxs > 0){
15079                 labelCfg.cls += ' col-xs-' + this.labelxs;
15080                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15081             }
15082                 
15083                 
15084         } else if ( this.fieldLabel.length) {
15085             cfg.cn = [
15086                 {
15087                    tag : 'i',
15088                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15089                    tooltip : 'This field is required'
15090                 },
15091                 {
15092                     tag: 'label',
15093                     cls : 'control-label',
15094                     html : this.fieldLabel
15095
15096                 },
15097                 {
15098                     cls : '', 
15099                     cn: [
15100                         combobox
15101                     ]
15102                 }
15103             ];
15104             
15105             if(this.indicatorpos == 'right'){
15106                 cfg.cn = [
15107                     {
15108                         tag: 'label',
15109                         cls : 'control-label',
15110                         html : this.fieldLabel,
15111                         cn : [
15112                             {
15113                                tag : 'i',
15114                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15115                                tooltip : 'This field is required'
15116                             }
15117                         ]
15118                     },
15119                     {
15120                         cls : '', 
15121                         cn: [
15122                             combobox
15123                         ]
15124                     }
15125                 ];
15126             }
15127         } else {
15128             cfg.cn = combobox;    
15129         }
15130         
15131         
15132         var settings = this;
15133         
15134         ['xs','sm','md','lg'].map(function(size){
15135             if (settings[size]) {
15136                 cfg.cls += ' col-' + size + '-' + settings[size];
15137             }
15138         });
15139         
15140         return cfg;
15141     },
15142     
15143     initTouchView : function()
15144     {
15145         this.renderTouchView();
15146         
15147         this.touchViewEl.on('scroll', function(){
15148             this.el.dom.scrollTop = 0;
15149         }, this);
15150         
15151         this.originalValue = this.getValue();
15152         
15153         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15154         
15155         this.inputEl().on("click", this.showTouchView, this);
15156         if (this.triggerEl) {
15157             this.triggerEl.on("click", this.showTouchView, this);
15158         }
15159         
15160         
15161         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15162         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15163         
15164         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15165         
15166         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15167         this.store.on('load', this.onTouchViewLoad, this);
15168         this.store.on('loadexception', this.onTouchViewLoadException, this);
15169         
15170         if(this.hiddenName){
15171             
15172             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15173             
15174             this.hiddenField.dom.value =
15175                 this.hiddenValue !== undefined ? this.hiddenValue :
15176                 this.value !== undefined ? this.value : '';
15177         
15178             this.el.dom.removeAttribute('name');
15179             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15180         }
15181         
15182         if(this.multiple){
15183             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15184             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15185         }
15186         
15187         if(this.removable && !this.multiple){
15188             var close = this.closeTriggerEl();
15189             if(close){
15190                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15191                 close.on('click', this.removeBtnClick, this, close);
15192             }
15193         }
15194         /*
15195          * fix the bug in Safari iOS8
15196          */
15197         this.inputEl().on("focus", function(e){
15198             document.activeElement.blur();
15199         }, this);
15200         
15201         return;
15202         
15203         
15204     },
15205     
15206     renderTouchView : function()
15207     {
15208         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15209         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15210         
15211         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15212         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15213         
15214         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15215         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15216         this.touchViewBodyEl.setStyle('overflow', 'auto');
15217         
15218         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15219         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15220         
15221         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15222         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15223         
15224     },
15225     
15226     showTouchView : function()
15227     {
15228         if(this.disabled){
15229             return;
15230         }
15231         
15232         this.touchViewHeaderEl.hide();
15233
15234         if(this.modalTitle.length){
15235             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15236             this.touchViewHeaderEl.show();
15237         }
15238
15239         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15240         this.touchViewEl.show();
15241
15242         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15243         
15244         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15245         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15246
15247         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15248
15249         if(this.modalTitle.length){
15250             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15251         }
15252         
15253         this.touchViewBodyEl.setHeight(bodyHeight);
15254
15255         if(this.animate){
15256             var _this = this;
15257             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15258         }else{
15259             this.touchViewEl.addClass('in');
15260         }
15261
15262         this.doTouchViewQuery();
15263         
15264     },
15265     
15266     hideTouchView : function()
15267     {
15268         this.touchViewEl.removeClass('in');
15269
15270         if(this.animate){
15271             var _this = this;
15272             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15273         }else{
15274             this.touchViewEl.setStyle('display', 'none');
15275         }
15276         
15277     },
15278     
15279     setTouchViewValue : function()
15280     {
15281         if(this.multiple){
15282             this.clearItem();
15283         
15284             var _this = this;
15285
15286             Roo.each(this.tickItems, function(o){
15287                 this.addItem(o);
15288             }, this);
15289         }
15290         
15291         this.hideTouchView();
15292     },
15293     
15294     doTouchViewQuery : function()
15295     {
15296         var qe = {
15297             query: '',
15298             forceAll: true,
15299             combo: this,
15300             cancel:false
15301         };
15302         
15303         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15304             return false;
15305         }
15306         
15307         if(!this.alwaysQuery || this.mode == 'local'){
15308             this.onTouchViewLoad();
15309             return;
15310         }
15311         
15312         this.store.load();
15313     },
15314     
15315     onTouchViewBeforeLoad : function(combo,opts)
15316     {
15317         return;
15318     },
15319
15320     // private
15321     onTouchViewLoad : function()
15322     {
15323         if(this.store.getCount() < 1){
15324             this.onTouchViewEmptyResults();
15325             return;
15326         }
15327         
15328         this.clearTouchView();
15329         
15330         var rawValue = this.getRawValue();
15331         
15332         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15333         
15334         this.tickItems = [];
15335         
15336         this.store.data.each(function(d, rowIndex){
15337             var row = this.touchViewListGroup.createChild(template);
15338             
15339             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15340                 row.addClass(d.data.cls);
15341             }
15342             
15343             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15344                 var cfg = {
15345                     data : d.data,
15346                     html : d.data[this.displayField]
15347                 };
15348                 
15349                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15350                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15351                 }
15352             }
15353             row.removeClass('selected');
15354             if(!this.multiple && this.valueField &&
15355                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15356             {
15357                 // radio buttons..
15358                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15359                 row.addClass('selected');
15360             }
15361             
15362             if(this.multiple && this.valueField &&
15363                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15364             {
15365                 
15366                 // checkboxes...
15367                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15368                 this.tickItems.push(d.data);
15369             }
15370             
15371             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15372             
15373         }, this);
15374         
15375         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15376         
15377         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15378
15379         if(this.modalTitle.length){
15380             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15381         }
15382
15383         var listHeight = this.touchViewListGroup.getHeight();
15384         
15385         var _this = this;
15386         
15387         if(firstChecked && listHeight > bodyHeight){
15388             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15389         }
15390         
15391     },
15392     
15393     onTouchViewLoadException : function()
15394     {
15395         this.hideTouchView();
15396     },
15397     
15398     onTouchViewEmptyResults : function()
15399     {
15400         this.clearTouchView();
15401         
15402         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15403         
15404         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15405         
15406     },
15407     
15408     clearTouchView : function()
15409     {
15410         this.touchViewListGroup.dom.innerHTML = '';
15411     },
15412     
15413     onTouchViewClick : function(e, el, o)
15414     {
15415         e.preventDefault();
15416         
15417         var row = o.row;
15418         var rowIndex = o.rowIndex;
15419         
15420         var r = this.store.getAt(rowIndex);
15421         
15422         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15423             
15424             if(!this.multiple){
15425                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15426                     c.dom.removeAttribute('checked');
15427                 }, this);
15428
15429                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15430
15431                 this.setFromData(r.data);
15432
15433                 var close = this.closeTriggerEl();
15434
15435                 if(close){
15436                     close.show();
15437                 }
15438
15439                 this.hideTouchView();
15440
15441                 this.fireEvent('select', this, r, rowIndex);
15442
15443                 return;
15444             }
15445
15446             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15447                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15448                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15449                 return;
15450             }
15451
15452             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15453             this.addItem(r.data);
15454             this.tickItems.push(r.data);
15455         }
15456     },
15457     
15458     getAutoCreateNativeIOS : function()
15459     {
15460         var cfg = {
15461             cls: 'form-group' //input-group,
15462         };
15463         
15464         var combobox =  {
15465             tag: 'select',
15466             cls : 'roo-ios-select'
15467         };
15468         
15469         if (this.name) {
15470             combobox.name = this.name;
15471         }
15472         
15473         if (this.disabled) {
15474             combobox.disabled = true;
15475         }
15476         
15477         var settings = this;
15478         
15479         ['xs','sm','md','lg'].map(function(size){
15480             if (settings[size]) {
15481                 cfg.cls += ' col-' + size + '-' + settings[size];
15482             }
15483         });
15484         
15485         cfg.cn = combobox;
15486         
15487         return cfg;
15488         
15489     },
15490     
15491     initIOSView : function()
15492     {
15493         this.store.on('load', this.onIOSViewLoad, this);
15494         
15495         return;
15496     },
15497     
15498     onIOSViewLoad : function()
15499     {
15500         if(this.store.getCount() < 1){
15501             return;
15502         }
15503         
15504         this.clearIOSView();
15505         
15506         if(this.allowBlank) {
15507             
15508             var default_text = '-- SELECT --';
15509             
15510             if(this.placeholder.length){
15511                 default_text = this.placeholder;
15512             }
15513             
15514             if(this.emptyTitle.length){
15515                 default_text += ' - ' + this.emptyTitle + ' -';
15516             }
15517             
15518             var opt = this.inputEl().createChild({
15519                 tag: 'option',
15520                 value : 0,
15521                 html : default_text
15522             });
15523             
15524             var o = {};
15525             o[this.valueField] = 0;
15526             o[this.displayField] = default_text;
15527             
15528             this.ios_options.push({
15529                 data : o,
15530                 el : opt
15531             });
15532             
15533         }
15534         
15535         this.store.data.each(function(d, rowIndex){
15536             
15537             var html = '';
15538             
15539             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15540                 html = d.data[this.displayField];
15541             }
15542             
15543             var value = '';
15544             
15545             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15546                 value = d.data[this.valueField];
15547             }
15548             
15549             var option = {
15550                 tag: 'option',
15551                 value : value,
15552                 html : html
15553             };
15554             
15555             if(this.value == d.data[this.valueField]){
15556                 option['selected'] = true;
15557             }
15558             
15559             var opt = this.inputEl().createChild(option);
15560             
15561             this.ios_options.push({
15562                 data : d.data,
15563                 el : opt
15564             });
15565             
15566         }, this);
15567         
15568         this.inputEl().on('change', function(){
15569            this.fireEvent('select', this);
15570         }, this);
15571         
15572     },
15573     
15574     clearIOSView: function()
15575     {
15576         this.inputEl().dom.innerHTML = '';
15577         
15578         this.ios_options = [];
15579     },
15580     
15581     setIOSValue: function(v)
15582     {
15583         this.value = v;
15584         
15585         if(!this.ios_options){
15586             return;
15587         }
15588         
15589         Roo.each(this.ios_options, function(opts){
15590            
15591            opts.el.dom.removeAttribute('selected');
15592            
15593            if(opts.data[this.valueField] != v){
15594                return;
15595            }
15596            
15597            opts.el.dom.setAttribute('selected', true);
15598            
15599         }, this);
15600     }
15601
15602     /** 
15603     * @cfg {Boolean} grow 
15604     * @hide 
15605     */
15606     /** 
15607     * @cfg {Number} growMin 
15608     * @hide 
15609     */
15610     /** 
15611     * @cfg {Number} growMax 
15612     * @hide 
15613     */
15614     /**
15615      * @hide
15616      * @method autoSize
15617      */
15618 });
15619
15620 Roo.apply(Roo.bootstrap.ComboBox,  {
15621     
15622     header : {
15623         tag: 'div',
15624         cls: 'modal-header',
15625         cn: [
15626             {
15627                 tag: 'h4',
15628                 cls: 'modal-title'
15629             }
15630         ]
15631     },
15632     
15633     body : {
15634         tag: 'div',
15635         cls: 'modal-body',
15636         cn: [
15637             {
15638                 tag: 'ul',
15639                 cls: 'list-group'
15640             }
15641         ]
15642     },
15643     
15644     listItemRadio : {
15645         tag: 'li',
15646         cls: 'list-group-item',
15647         cn: [
15648             {
15649                 tag: 'span',
15650                 cls: 'roo-combobox-list-group-item-value'
15651             },
15652             {
15653                 tag: 'div',
15654                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15655                 cn: [
15656                     {
15657                         tag: 'input',
15658                         type: 'radio'
15659                     },
15660                     {
15661                         tag: 'label'
15662                     }
15663                 ]
15664             }
15665         ]
15666     },
15667     
15668     listItemCheckbox : {
15669         tag: 'li',
15670         cls: 'list-group-item',
15671         cn: [
15672             {
15673                 tag: 'span',
15674                 cls: 'roo-combobox-list-group-item-value'
15675             },
15676             {
15677                 tag: 'div',
15678                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15679                 cn: [
15680                     {
15681                         tag: 'input',
15682                         type: 'checkbox'
15683                     },
15684                     {
15685                         tag: 'label'
15686                     }
15687                 ]
15688             }
15689         ]
15690     },
15691     
15692     emptyResult : {
15693         tag: 'div',
15694         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15695     },
15696     
15697     footer : {
15698         tag: 'div',
15699         cls: 'modal-footer',
15700         cn: [
15701             {
15702                 tag: 'div',
15703                 cls: 'row',
15704                 cn: [
15705                     {
15706                         tag: 'div',
15707                         cls: 'col-xs-6 text-left',
15708                         cn: {
15709                             tag: 'button',
15710                             cls: 'btn btn-danger roo-touch-view-cancel',
15711                             html: 'Cancel'
15712                         }
15713                     },
15714                     {
15715                         tag: 'div',
15716                         cls: 'col-xs-6 text-right',
15717                         cn: {
15718                             tag: 'button',
15719                             cls: 'btn btn-success roo-touch-view-ok',
15720                             html: 'OK'
15721                         }
15722                     }
15723                 ]
15724             }
15725         ]
15726         
15727     }
15728 });
15729
15730 Roo.apply(Roo.bootstrap.ComboBox,  {
15731     
15732     touchViewTemplate : {
15733         tag: 'div',
15734         cls: 'modal fade roo-combobox-touch-view',
15735         cn: [
15736             {
15737                 tag: 'div',
15738                 cls: 'modal-dialog',
15739                 style : 'position:fixed', // we have to fix position....
15740                 cn: [
15741                     {
15742                         tag: 'div',
15743                         cls: 'modal-content',
15744                         cn: [
15745                             Roo.bootstrap.ComboBox.header,
15746                             Roo.bootstrap.ComboBox.body,
15747                             Roo.bootstrap.ComboBox.footer
15748                         ]
15749                     }
15750                 ]
15751             }
15752         ]
15753     }
15754 });/*
15755  * Based on:
15756  * Ext JS Library 1.1.1
15757  * Copyright(c) 2006-2007, Ext JS, LLC.
15758  *
15759  * Originally Released Under LGPL - original licence link has changed is not relivant.
15760  *
15761  * Fork - LGPL
15762  * <script type="text/javascript">
15763  */
15764
15765 /**
15766  * @class Roo.View
15767  * @extends Roo.util.Observable
15768  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15769  * This class also supports single and multi selection modes. <br>
15770  * Create a data model bound view:
15771  <pre><code>
15772  var store = new Roo.data.Store(...);
15773
15774  var view = new Roo.View({
15775     el : "my-element",
15776     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15777  
15778     singleSelect: true,
15779     selectedClass: "ydataview-selected",
15780     store: store
15781  });
15782
15783  // listen for node click?
15784  view.on("click", function(vw, index, node, e){
15785  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15786  });
15787
15788  // load XML data
15789  dataModel.load("foobar.xml");
15790  </code></pre>
15791  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15792  * <br><br>
15793  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15794  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15795  * 
15796  * Note: old style constructor is still suported (container, template, config)
15797  * 
15798  * @constructor
15799  * Create a new View
15800  * @param {Object} config The config object
15801  * 
15802  */
15803 Roo.View = function(config, depreciated_tpl, depreciated_config){
15804     
15805     this.parent = false;
15806     
15807     if (typeof(depreciated_tpl) == 'undefined') {
15808         // new way.. - universal constructor.
15809         Roo.apply(this, config);
15810         this.el  = Roo.get(this.el);
15811     } else {
15812         // old format..
15813         this.el  = Roo.get(config);
15814         this.tpl = depreciated_tpl;
15815         Roo.apply(this, depreciated_config);
15816     }
15817     this.wrapEl  = this.el.wrap().wrap();
15818     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15819     
15820     
15821     if(typeof(this.tpl) == "string"){
15822         this.tpl = new Roo.Template(this.tpl);
15823     } else {
15824         // support xtype ctors..
15825         this.tpl = new Roo.factory(this.tpl, Roo);
15826     }
15827     
15828     
15829     this.tpl.compile();
15830     
15831     /** @private */
15832     this.addEvents({
15833         /**
15834          * @event beforeclick
15835          * Fires before a click is processed. Returns false to cancel the default action.
15836          * @param {Roo.View} this
15837          * @param {Number} index The index of the target node
15838          * @param {HTMLElement} node The target node
15839          * @param {Roo.EventObject} e The raw event object
15840          */
15841             "beforeclick" : true,
15842         /**
15843          * @event click
15844          * Fires when a template node is clicked.
15845          * @param {Roo.View} this
15846          * @param {Number} index The index of the target node
15847          * @param {HTMLElement} node The target node
15848          * @param {Roo.EventObject} e The raw event object
15849          */
15850             "click" : true,
15851         /**
15852          * @event dblclick
15853          * Fires when a template node is double clicked.
15854          * @param {Roo.View} this
15855          * @param {Number} index The index of the target node
15856          * @param {HTMLElement} node The target node
15857          * @param {Roo.EventObject} e The raw event object
15858          */
15859             "dblclick" : true,
15860         /**
15861          * @event contextmenu
15862          * Fires when a template node is right clicked.
15863          * @param {Roo.View} this
15864          * @param {Number} index The index of the target node
15865          * @param {HTMLElement} node The target node
15866          * @param {Roo.EventObject} e The raw event object
15867          */
15868             "contextmenu" : true,
15869         /**
15870          * @event selectionchange
15871          * Fires when the selected nodes change.
15872          * @param {Roo.View} this
15873          * @param {Array} selections Array of the selected nodes
15874          */
15875             "selectionchange" : true,
15876     
15877         /**
15878          * @event beforeselect
15879          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15880          * @param {Roo.View} this
15881          * @param {HTMLElement} node The node to be selected
15882          * @param {Array} selections Array of currently selected nodes
15883          */
15884             "beforeselect" : true,
15885         /**
15886          * @event preparedata
15887          * Fires on every row to render, to allow you to change the data.
15888          * @param {Roo.View} this
15889          * @param {Object} data to be rendered (change this)
15890          */
15891           "preparedata" : true
15892           
15893           
15894         });
15895
15896
15897
15898     this.el.on({
15899         "click": this.onClick,
15900         "dblclick": this.onDblClick,
15901         "contextmenu": this.onContextMenu,
15902         scope:this
15903     });
15904
15905     this.selections = [];
15906     this.nodes = [];
15907     this.cmp = new Roo.CompositeElementLite([]);
15908     if(this.store){
15909         this.store = Roo.factory(this.store, Roo.data);
15910         this.setStore(this.store, true);
15911     }
15912     
15913     if ( this.footer && this.footer.xtype) {
15914            
15915          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15916         
15917         this.footer.dataSource = this.store;
15918         this.footer.container = fctr;
15919         this.footer = Roo.factory(this.footer, Roo);
15920         fctr.insertFirst(this.el);
15921         
15922         // this is a bit insane - as the paging toolbar seems to detach the el..
15923 //        dom.parentNode.parentNode.parentNode
15924          // they get detached?
15925     }
15926     
15927     
15928     Roo.View.superclass.constructor.call(this);
15929     
15930     
15931 };
15932
15933 Roo.extend(Roo.View, Roo.util.Observable, {
15934     
15935      /**
15936      * @cfg {Roo.data.Store} store Data store to load data from.
15937      */
15938     store : false,
15939     
15940     /**
15941      * @cfg {String|Roo.Element} el The container element.
15942      */
15943     el : '',
15944     
15945     /**
15946      * @cfg {String|Roo.Template} tpl The template used by this View 
15947      */
15948     tpl : false,
15949     /**
15950      * @cfg {String} dataName the named area of the template to use as the data area
15951      *                          Works with domtemplates roo-name="name"
15952      */
15953     dataName: false,
15954     /**
15955      * @cfg {String} selectedClass The css class to add to selected nodes
15956      */
15957     selectedClass : "x-view-selected",
15958      /**
15959      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15960      */
15961     emptyText : "",
15962     
15963     /**
15964      * @cfg {String} text to display on mask (default Loading)
15965      */
15966     mask : false,
15967     /**
15968      * @cfg {Boolean} multiSelect Allow multiple selection
15969      */
15970     multiSelect : false,
15971     /**
15972      * @cfg {Boolean} singleSelect Allow single selection
15973      */
15974     singleSelect:  false,
15975     
15976     /**
15977      * @cfg {Boolean} toggleSelect - selecting 
15978      */
15979     toggleSelect : false,
15980     
15981     /**
15982      * @cfg {Boolean} tickable - selecting 
15983      */
15984     tickable : false,
15985     
15986     /**
15987      * Returns the element this view is bound to.
15988      * @return {Roo.Element}
15989      */
15990     getEl : function(){
15991         return this.wrapEl;
15992     },
15993     
15994     
15995
15996     /**
15997      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15998      */
15999     refresh : function(){
16000         //Roo.log('refresh');
16001         var t = this.tpl;
16002         
16003         // if we are using something like 'domtemplate', then
16004         // the what gets used is:
16005         // t.applySubtemplate(NAME, data, wrapping data..)
16006         // the outer template then get' applied with
16007         //     the store 'extra data'
16008         // and the body get's added to the
16009         //      roo-name="data" node?
16010         //      <span class='roo-tpl-{name}'></span> ?????
16011         
16012         
16013         
16014         this.clearSelections();
16015         this.el.update("");
16016         var html = [];
16017         var records = this.store.getRange();
16018         if(records.length < 1) {
16019             
16020             // is this valid??  = should it render a template??
16021             
16022             this.el.update(this.emptyText);
16023             return;
16024         }
16025         var el = this.el;
16026         if (this.dataName) {
16027             this.el.update(t.apply(this.store.meta)); //????
16028             el = this.el.child('.roo-tpl-' + this.dataName);
16029         }
16030         
16031         for(var i = 0, len = records.length; i < len; i++){
16032             var data = this.prepareData(records[i].data, i, records[i]);
16033             this.fireEvent("preparedata", this, data, i, records[i]);
16034             
16035             var d = Roo.apply({}, data);
16036             
16037             if(this.tickable){
16038                 Roo.apply(d, {'roo-id' : Roo.id()});
16039                 
16040                 var _this = this;
16041             
16042                 Roo.each(this.parent.item, function(item){
16043                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16044                         return;
16045                     }
16046                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16047                 });
16048             }
16049             
16050             html[html.length] = Roo.util.Format.trim(
16051                 this.dataName ?
16052                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16053                     t.apply(d)
16054             );
16055         }
16056         
16057         
16058         
16059         el.update(html.join(""));
16060         this.nodes = el.dom.childNodes;
16061         this.updateIndexes(0);
16062     },
16063     
16064
16065     /**
16066      * Function to override to reformat the data that is sent to
16067      * the template for each node.
16068      * DEPRICATED - use the preparedata event handler.
16069      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16070      * a JSON object for an UpdateManager bound view).
16071      */
16072     prepareData : function(data, index, record)
16073     {
16074         this.fireEvent("preparedata", this, data, index, record);
16075         return data;
16076     },
16077
16078     onUpdate : function(ds, record){
16079         // Roo.log('on update');   
16080         this.clearSelections();
16081         var index = this.store.indexOf(record);
16082         var n = this.nodes[index];
16083         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16084         n.parentNode.removeChild(n);
16085         this.updateIndexes(index, index);
16086     },
16087
16088     
16089     
16090 // --------- FIXME     
16091     onAdd : function(ds, records, index)
16092     {
16093         //Roo.log(['on Add', ds, records, index] );        
16094         this.clearSelections();
16095         if(this.nodes.length == 0){
16096             this.refresh();
16097             return;
16098         }
16099         var n = this.nodes[index];
16100         for(var i = 0, len = records.length; i < len; i++){
16101             var d = this.prepareData(records[i].data, i, records[i]);
16102             if(n){
16103                 this.tpl.insertBefore(n, d);
16104             }else{
16105                 
16106                 this.tpl.append(this.el, d);
16107             }
16108         }
16109         this.updateIndexes(index);
16110     },
16111
16112     onRemove : function(ds, record, index){
16113        // Roo.log('onRemove');
16114         this.clearSelections();
16115         var el = this.dataName  ?
16116             this.el.child('.roo-tpl-' + this.dataName) :
16117             this.el; 
16118         
16119         el.dom.removeChild(this.nodes[index]);
16120         this.updateIndexes(index);
16121     },
16122
16123     /**
16124      * Refresh an individual node.
16125      * @param {Number} index
16126      */
16127     refreshNode : function(index){
16128         this.onUpdate(this.store, this.store.getAt(index));
16129     },
16130
16131     updateIndexes : function(startIndex, endIndex){
16132         var ns = this.nodes;
16133         startIndex = startIndex || 0;
16134         endIndex = endIndex || ns.length - 1;
16135         for(var i = startIndex; i <= endIndex; i++){
16136             ns[i].nodeIndex = i;
16137         }
16138     },
16139
16140     /**
16141      * Changes the data store this view uses and refresh the view.
16142      * @param {Store} store
16143      */
16144     setStore : function(store, initial){
16145         if(!initial && this.store){
16146             this.store.un("datachanged", this.refresh);
16147             this.store.un("add", this.onAdd);
16148             this.store.un("remove", this.onRemove);
16149             this.store.un("update", this.onUpdate);
16150             this.store.un("clear", this.refresh);
16151             this.store.un("beforeload", this.onBeforeLoad);
16152             this.store.un("load", this.onLoad);
16153             this.store.un("loadexception", this.onLoad);
16154         }
16155         if(store){
16156           
16157             store.on("datachanged", this.refresh, this);
16158             store.on("add", this.onAdd, this);
16159             store.on("remove", this.onRemove, this);
16160             store.on("update", this.onUpdate, this);
16161             store.on("clear", this.refresh, this);
16162             store.on("beforeload", this.onBeforeLoad, this);
16163             store.on("load", this.onLoad, this);
16164             store.on("loadexception", this.onLoad, this);
16165         }
16166         
16167         if(store){
16168             this.refresh();
16169         }
16170     },
16171     /**
16172      * onbeforeLoad - masks the loading area.
16173      *
16174      */
16175     onBeforeLoad : function(store,opts)
16176     {
16177          //Roo.log('onBeforeLoad');   
16178         if (!opts.add) {
16179             this.el.update("");
16180         }
16181         this.el.mask(this.mask ? this.mask : "Loading" ); 
16182     },
16183     onLoad : function ()
16184     {
16185         this.el.unmask();
16186     },
16187     
16188
16189     /**
16190      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16191      * @param {HTMLElement} node
16192      * @return {HTMLElement} The template node
16193      */
16194     findItemFromChild : function(node){
16195         var el = this.dataName  ?
16196             this.el.child('.roo-tpl-' + this.dataName,true) :
16197             this.el.dom; 
16198         
16199         if(!node || node.parentNode == el){
16200                     return node;
16201             }
16202             var p = node.parentNode;
16203             while(p && p != el){
16204             if(p.parentNode == el){
16205                 return p;
16206             }
16207             p = p.parentNode;
16208         }
16209             return null;
16210     },
16211
16212     /** @ignore */
16213     onClick : function(e){
16214         var item = this.findItemFromChild(e.getTarget());
16215         if(item){
16216             var index = this.indexOf(item);
16217             if(this.onItemClick(item, index, e) !== false){
16218                 this.fireEvent("click", this, index, item, e);
16219             }
16220         }else{
16221             this.clearSelections();
16222         }
16223     },
16224
16225     /** @ignore */
16226     onContextMenu : function(e){
16227         var item = this.findItemFromChild(e.getTarget());
16228         if(item){
16229             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16230         }
16231     },
16232
16233     /** @ignore */
16234     onDblClick : function(e){
16235         var item = this.findItemFromChild(e.getTarget());
16236         if(item){
16237             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16238         }
16239     },
16240
16241     onItemClick : function(item, index, e)
16242     {
16243         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16244             return false;
16245         }
16246         if (this.toggleSelect) {
16247             var m = this.isSelected(item) ? 'unselect' : 'select';
16248             //Roo.log(m);
16249             var _t = this;
16250             _t[m](item, true, false);
16251             return true;
16252         }
16253         if(this.multiSelect || this.singleSelect){
16254             if(this.multiSelect && e.shiftKey && this.lastSelection){
16255                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16256             }else{
16257                 this.select(item, this.multiSelect && e.ctrlKey);
16258                 this.lastSelection = item;
16259             }
16260             
16261             if(!this.tickable){
16262                 e.preventDefault();
16263             }
16264             
16265         }
16266         return true;
16267     },
16268
16269     /**
16270      * Get the number of selected nodes.
16271      * @return {Number}
16272      */
16273     getSelectionCount : function(){
16274         return this.selections.length;
16275     },
16276
16277     /**
16278      * Get the currently selected nodes.
16279      * @return {Array} An array of HTMLElements
16280      */
16281     getSelectedNodes : function(){
16282         return this.selections;
16283     },
16284
16285     /**
16286      * Get the indexes of the selected nodes.
16287      * @return {Array}
16288      */
16289     getSelectedIndexes : function(){
16290         var indexes = [], s = this.selections;
16291         for(var i = 0, len = s.length; i < len; i++){
16292             indexes.push(s[i].nodeIndex);
16293         }
16294         return indexes;
16295     },
16296
16297     /**
16298      * Clear all selections
16299      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16300      */
16301     clearSelections : function(suppressEvent){
16302         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16303             this.cmp.elements = this.selections;
16304             this.cmp.removeClass(this.selectedClass);
16305             this.selections = [];
16306             if(!suppressEvent){
16307                 this.fireEvent("selectionchange", this, this.selections);
16308             }
16309         }
16310     },
16311
16312     /**
16313      * Returns true if the passed node is selected
16314      * @param {HTMLElement/Number} node The node or node index
16315      * @return {Boolean}
16316      */
16317     isSelected : function(node){
16318         var s = this.selections;
16319         if(s.length < 1){
16320             return false;
16321         }
16322         node = this.getNode(node);
16323         return s.indexOf(node) !== -1;
16324     },
16325
16326     /**
16327      * Selects nodes.
16328      * @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
16329      * @param {Boolean} keepExisting (optional) true to keep existing selections
16330      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16331      */
16332     select : function(nodeInfo, keepExisting, suppressEvent){
16333         if(nodeInfo instanceof Array){
16334             if(!keepExisting){
16335                 this.clearSelections(true);
16336             }
16337             for(var i = 0, len = nodeInfo.length; i < len; i++){
16338                 this.select(nodeInfo[i], true, true);
16339             }
16340             return;
16341         } 
16342         var node = this.getNode(nodeInfo);
16343         if(!node || this.isSelected(node)){
16344             return; // already selected.
16345         }
16346         if(!keepExisting){
16347             this.clearSelections(true);
16348         }
16349         
16350         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16351             Roo.fly(node).addClass(this.selectedClass);
16352             this.selections.push(node);
16353             if(!suppressEvent){
16354                 this.fireEvent("selectionchange", this, this.selections);
16355             }
16356         }
16357         
16358         
16359     },
16360       /**
16361      * Unselects nodes.
16362      * @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
16363      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16364      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16365      */
16366     unselect : function(nodeInfo, keepExisting, suppressEvent)
16367     {
16368         if(nodeInfo instanceof Array){
16369             Roo.each(this.selections, function(s) {
16370                 this.unselect(s, nodeInfo);
16371             }, this);
16372             return;
16373         }
16374         var node = this.getNode(nodeInfo);
16375         if(!node || !this.isSelected(node)){
16376             //Roo.log("not selected");
16377             return; // not selected.
16378         }
16379         // fireevent???
16380         var ns = [];
16381         Roo.each(this.selections, function(s) {
16382             if (s == node ) {
16383                 Roo.fly(node).removeClass(this.selectedClass);
16384
16385                 return;
16386             }
16387             ns.push(s);
16388         },this);
16389         
16390         this.selections= ns;
16391         this.fireEvent("selectionchange", this, this.selections);
16392     },
16393
16394     /**
16395      * Gets a template node.
16396      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16397      * @return {HTMLElement} The node or null if it wasn't found
16398      */
16399     getNode : function(nodeInfo){
16400         if(typeof nodeInfo == "string"){
16401             return document.getElementById(nodeInfo);
16402         }else if(typeof nodeInfo == "number"){
16403             return this.nodes[nodeInfo];
16404         }
16405         return nodeInfo;
16406     },
16407
16408     /**
16409      * Gets a range template nodes.
16410      * @param {Number} startIndex
16411      * @param {Number} endIndex
16412      * @return {Array} An array of nodes
16413      */
16414     getNodes : function(start, end){
16415         var ns = this.nodes;
16416         start = start || 0;
16417         end = typeof end == "undefined" ? ns.length - 1 : end;
16418         var nodes = [];
16419         if(start <= end){
16420             for(var i = start; i <= end; i++){
16421                 nodes.push(ns[i]);
16422             }
16423         } else{
16424             for(var i = start; i >= end; i--){
16425                 nodes.push(ns[i]);
16426             }
16427         }
16428         return nodes;
16429     },
16430
16431     /**
16432      * Finds the index of the passed node
16433      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16434      * @return {Number} The index of the node or -1
16435      */
16436     indexOf : function(node){
16437         node = this.getNode(node);
16438         if(typeof node.nodeIndex == "number"){
16439             return node.nodeIndex;
16440         }
16441         var ns = this.nodes;
16442         for(var i = 0, len = ns.length; i < len; i++){
16443             if(ns[i] == node){
16444                 return i;
16445             }
16446         }
16447         return -1;
16448     }
16449 });
16450 /*
16451  * - LGPL
16452  *
16453  * based on jquery fullcalendar
16454  * 
16455  */
16456
16457 Roo.bootstrap = Roo.bootstrap || {};
16458 /**
16459  * @class Roo.bootstrap.Calendar
16460  * @extends Roo.bootstrap.Component
16461  * Bootstrap Calendar class
16462  * @cfg {Boolean} loadMask (true|false) default false
16463  * @cfg {Object} header generate the user specific header of the calendar, default false
16464
16465  * @constructor
16466  * Create a new Container
16467  * @param {Object} config The config object
16468  */
16469
16470
16471
16472 Roo.bootstrap.Calendar = function(config){
16473     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16474      this.addEvents({
16475         /**
16476              * @event select
16477              * Fires when a date is selected
16478              * @param {DatePicker} this
16479              * @param {Date} date The selected date
16480              */
16481         'select': true,
16482         /**
16483              * @event monthchange
16484              * Fires when the displayed month changes 
16485              * @param {DatePicker} this
16486              * @param {Date} date The selected month
16487              */
16488         'monthchange': true,
16489         /**
16490              * @event evententer
16491              * Fires when mouse over an event
16492              * @param {Calendar} this
16493              * @param {event} Event
16494              */
16495         'evententer': true,
16496         /**
16497              * @event eventleave
16498              * Fires when the mouse leaves an
16499              * @param {Calendar} this
16500              * @param {event}
16501              */
16502         'eventleave': true,
16503         /**
16504              * @event eventclick
16505              * Fires when the mouse click an
16506              * @param {Calendar} this
16507              * @param {event}
16508              */
16509         'eventclick': true
16510         
16511     });
16512
16513 };
16514
16515 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16516     
16517      /**
16518      * @cfg {Number} startDay
16519      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16520      */
16521     startDay : 0,
16522     
16523     loadMask : false,
16524     
16525     header : false,
16526       
16527     getAutoCreate : function(){
16528         
16529         
16530         var fc_button = function(name, corner, style, content ) {
16531             return Roo.apply({},{
16532                 tag : 'span',
16533                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16534                          (corner.length ?
16535                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16536                             ''
16537                         ),
16538                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16539                 unselectable: 'on'
16540             });
16541         };
16542         
16543         var header = {};
16544         
16545         if(!this.header){
16546             header = {
16547                 tag : 'table',
16548                 cls : 'fc-header',
16549                 style : 'width:100%',
16550                 cn : [
16551                     {
16552                         tag: 'tr',
16553                         cn : [
16554                             {
16555                                 tag : 'td',
16556                                 cls : 'fc-header-left',
16557                                 cn : [
16558                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16559                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16560                                     { tag: 'span', cls: 'fc-header-space' },
16561                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16562
16563
16564                                 ]
16565                             },
16566
16567                             {
16568                                 tag : 'td',
16569                                 cls : 'fc-header-center',
16570                                 cn : [
16571                                     {
16572                                         tag: 'span',
16573                                         cls: 'fc-header-title',
16574                                         cn : {
16575                                             tag: 'H2',
16576                                             html : 'month / year'
16577                                         }
16578                                     }
16579
16580                                 ]
16581                             },
16582                             {
16583                                 tag : 'td',
16584                                 cls : 'fc-header-right',
16585                                 cn : [
16586                               /*      fc_button('month', 'left', '', 'month' ),
16587                                     fc_button('week', '', '', 'week' ),
16588                                     fc_button('day', 'right', '', 'day' )
16589                                 */    
16590
16591                                 ]
16592                             }
16593
16594                         ]
16595                     }
16596                 ]
16597             };
16598         }
16599         
16600         header = this.header;
16601         
16602        
16603         var cal_heads = function() {
16604             var ret = [];
16605             // fixme - handle this.
16606             
16607             for (var i =0; i < Date.dayNames.length; i++) {
16608                 var d = Date.dayNames[i];
16609                 ret.push({
16610                     tag: 'th',
16611                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16612                     html : d.substring(0,3)
16613                 });
16614                 
16615             }
16616             ret[0].cls += ' fc-first';
16617             ret[6].cls += ' fc-last';
16618             return ret;
16619         };
16620         var cal_cell = function(n) {
16621             return  {
16622                 tag: 'td',
16623                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16624                 cn : [
16625                     {
16626                         cn : [
16627                             {
16628                                 cls: 'fc-day-number',
16629                                 html: 'D'
16630                             },
16631                             {
16632                                 cls: 'fc-day-content',
16633                              
16634                                 cn : [
16635                                      {
16636                                         style: 'position: relative;' // height: 17px;
16637                                     }
16638                                 ]
16639                             }
16640                             
16641                             
16642                         ]
16643                     }
16644                 ]
16645                 
16646             }
16647         };
16648         var cal_rows = function() {
16649             
16650             var ret = [];
16651             for (var r = 0; r < 6; r++) {
16652                 var row= {
16653                     tag : 'tr',
16654                     cls : 'fc-week',
16655                     cn : []
16656                 };
16657                 
16658                 for (var i =0; i < Date.dayNames.length; i++) {
16659                     var d = Date.dayNames[i];
16660                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16661
16662                 }
16663                 row.cn[0].cls+=' fc-first';
16664                 row.cn[0].cn[0].style = 'min-height:90px';
16665                 row.cn[6].cls+=' fc-last';
16666                 ret.push(row);
16667                 
16668             }
16669             ret[0].cls += ' fc-first';
16670             ret[4].cls += ' fc-prev-last';
16671             ret[5].cls += ' fc-last';
16672             return ret;
16673             
16674         };
16675         
16676         var cal_table = {
16677             tag: 'table',
16678             cls: 'fc-border-separate',
16679             style : 'width:100%',
16680             cellspacing  : 0,
16681             cn : [
16682                 { 
16683                     tag: 'thead',
16684                     cn : [
16685                         { 
16686                             tag: 'tr',
16687                             cls : 'fc-first fc-last',
16688                             cn : cal_heads()
16689                         }
16690                     ]
16691                 },
16692                 { 
16693                     tag: 'tbody',
16694                     cn : cal_rows()
16695                 }
16696                   
16697             ]
16698         };
16699          
16700          var cfg = {
16701             cls : 'fc fc-ltr',
16702             cn : [
16703                 header,
16704                 {
16705                     cls : 'fc-content',
16706                     style : "position: relative;",
16707                     cn : [
16708                         {
16709                             cls : 'fc-view fc-view-month fc-grid',
16710                             style : 'position: relative',
16711                             unselectable : 'on',
16712                             cn : [
16713                                 {
16714                                     cls : 'fc-event-container',
16715                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16716                                 },
16717                                 cal_table
16718                             ]
16719                         }
16720                     ]
16721     
16722                 }
16723            ] 
16724             
16725         };
16726         
16727          
16728         
16729         return cfg;
16730     },
16731     
16732     
16733     initEvents : function()
16734     {
16735         if(!this.store){
16736             throw "can not find store for calendar";
16737         }
16738         
16739         var mark = {
16740             tag: "div",
16741             cls:"x-dlg-mask",
16742             style: "text-align:center",
16743             cn: [
16744                 {
16745                     tag: "div",
16746                     style: "background-color:white;width:50%;margin:250 auto",
16747                     cn: [
16748                         {
16749                             tag: "img",
16750                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16751                         },
16752                         {
16753                             tag: "span",
16754                             html: "Loading"
16755                         }
16756                         
16757                     ]
16758                 }
16759             ]
16760         };
16761         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16762         
16763         var size = this.el.select('.fc-content', true).first().getSize();
16764         this.maskEl.setSize(size.width, size.height);
16765         this.maskEl.enableDisplayMode("block");
16766         if(!this.loadMask){
16767             this.maskEl.hide();
16768         }
16769         
16770         this.store = Roo.factory(this.store, Roo.data);
16771         this.store.on('load', this.onLoad, this);
16772         this.store.on('beforeload', this.onBeforeLoad, this);
16773         
16774         this.resize();
16775         
16776         this.cells = this.el.select('.fc-day',true);
16777         //Roo.log(this.cells);
16778         this.textNodes = this.el.query('.fc-day-number');
16779         this.cells.addClassOnOver('fc-state-hover');
16780         
16781         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16782         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16783         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16784         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16785         
16786         this.on('monthchange', this.onMonthChange, this);
16787         
16788         this.update(new Date().clearTime());
16789     },
16790     
16791     resize : function() {
16792         var sz  = this.el.getSize();
16793         
16794         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16795         this.el.select('.fc-day-content div',true).setHeight(34);
16796     },
16797     
16798     
16799     // private
16800     showPrevMonth : function(e){
16801         this.update(this.activeDate.add("mo", -1));
16802     },
16803     showToday : function(e){
16804         this.update(new Date().clearTime());
16805     },
16806     // private
16807     showNextMonth : function(e){
16808         this.update(this.activeDate.add("mo", 1));
16809     },
16810
16811     // private
16812     showPrevYear : function(){
16813         this.update(this.activeDate.add("y", -1));
16814     },
16815
16816     // private
16817     showNextYear : function(){
16818         this.update(this.activeDate.add("y", 1));
16819     },
16820
16821     
16822    // private
16823     update : function(date)
16824     {
16825         var vd = this.activeDate;
16826         this.activeDate = date;
16827 //        if(vd && this.el){
16828 //            var t = date.getTime();
16829 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16830 //                Roo.log('using add remove');
16831 //                
16832 //                this.fireEvent('monthchange', this, date);
16833 //                
16834 //                this.cells.removeClass("fc-state-highlight");
16835 //                this.cells.each(function(c){
16836 //                   if(c.dateValue == t){
16837 //                       c.addClass("fc-state-highlight");
16838 //                       setTimeout(function(){
16839 //                            try{c.dom.firstChild.focus();}catch(e){}
16840 //                       }, 50);
16841 //                       return false;
16842 //                   }
16843 //                   return true;
16844 //                });
16845 //                return;
16846 //            }
16847 //        }
16848         
16849         var days = date.getDaysInMonth();
16850         
16851         var firstOfMonth = date.getFirstDateOfMonth();
16852         var startingPos = firstOfMonth.getDay()-this.startDay;
16853         
16854         if(startingPos < this.startDay){
16855             startingPos += 7;
16856         }
16857         
16858         var pm = date.add(Date.MONTH, -1);
16859         var prevStart = pm.getDaysInMonth()-startingPos;
16860 //        
16861         this.cells = this.el.select('.fc-day',true);
16862         this.textNodes = this.el.query('.fc-day-number');
16863         this.cells.addClassOnOver('fc-state-hover');
16864         
16865         var cells = this.cells.elements;
16866         var textEls = this.textNodes;
16867         
16868         Roo.each(cells, function(cell){
16869             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16870         });
16871         
16872         days += startingPos;
16873
16874         // convert everything to numbers so it's fast
16875         var day = 86400000;
16876         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16877         //Roo.log(d);
16878         //Roo.log(pm);
16879         //Roo.log(prevStart);
16880         
16881         var today = new Date().clearTime().getTime();
16882         var sel = date.clearTime().getTime();
16883         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16884         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16885         var ddMatch = this.disabledDatesRE;
16886         var ddText = this.disabledDatesText;
16887         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16888         var ddaysText = this.disabledDaysText;
16889         var format = this.format;
16890         
16891         var setCellClass = function(cal, cell){
16892             cell.row = 0;
16893             cell.events = [];
16894             cell.more = [];
16895             //Roo.log('set Cell Class');
16896             cell.title = "";
16897             var t = d.getTime();
16898             
16899             //Roo.log(d);
16900             
16901             cell.dateValue = t;
16902             if(t == today){
16903                 cell.className += " fc-today";
16904                 cell.className += " fc-state-highlight";
16905                 cell.title = cal.todayText;
16906             }
16907             if(t == sel){
16908                 // disable highlight in other month..
16909                 //cell.className += " fc-state-highlight";
16910                 
16911             }
16912             // disabling
16913             if(t < min) {
16914                 cell.className = " fc-state-disabled";
16915                 cell.title = cal.minText;
16916                 return;
16917             }
16918             if(t > max) {
16919                 cell.className = " fc-state-disabled";
16920                 cell.title = cal.maxText;
16921                 return;
16922             }
16923             if(ddays){
16924                 if(ddays.indexOf(d.getDay()) != -1){
16925                     cell.title = ddaysText;
16926                     cell.className = " fc-state-disabled";
16927                 }
16928             }
16929             if(ddMatch && format){
16930                 var fvalue = d.dateFormat(format);
16931                 if(ddMatch.test(fvalue)){
16932                     cell.title = ddText.replace("%0", fvalue);
16933                     cell.className = " fc-state-disabled";
16934                 }
16935             }
16936             
16937             if (!cell.initialClassName) {
16938                 cell.initialClassName = cell.dom.className;
16939             }
16940             
16941             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16942         };
16943
16944         var i = 0;
16945         
16946         for(; i < startingPos; i++) {
16947             textEls[i].innerHTML = (++prevStart);
16948             d.setDate(d.getDate()+1);
16949             
16950             cells[i].className = "fc-past fc-other-month";
16951             setCellClass(this, cells[i]);
16952         }
16953         
16954         var intDay = 0;
16955         
16956         for(; i < days; i++){
16957             intDay = i - startingPos + 1;
16958             textEls[i].innerHTML = (intDay);
16959             d.setDate(d.getDate()+1);
16960             
16961             cells[i].className = ''; // "x-date-active";
16962             setCellClass(this, cells[i]);
16963         }
16964         var extraDays = 0;
16965         
16966         for(; i < 42; i++) {
16967             textEls[i].innerHTML = (++extraDays);
16968             d.setDate(d.getDate()+1);
16969             
16970             cells[i].className = "fc-future fc-other-month";
16971             setCellClass(this, cells[i]);
16972         }
16973         
16974         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16975         
16976         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16977         
16978         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16979         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16980         
16981         if(totalRows != 6){
16982             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16983             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16984         }
16985         
16986         this.fireEvent('monthchange', this, date);
16987         
16988         
16989         /*
16990         if(!this.internalRender){
16991             var main = this.el.dom.firstChild;
16992             var w = main.offsetWidth;
16993             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16994             Roo.fly(main).setWidth(w);
16995             this.internalRender = true;
16996             // opera does not respect the auto grow header center column
16997             // then, after it gets a width opera refuses to recalculate
16998             // without a second pass
16999             if(Roo.isOpera && !this.secondPass){
17000                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17001                 this.secondPass = true;
17002                 this.update.defer(10, this, [date]);
17003             }
17004         }
17005         */
17006         
17007     },
17008     
17009     findCell : function(dt) {
17010         dt = dt.clearTime().getTime();
17011         var ret = false;
17012         this.cells.each(function(c){
17013             //Roo.log("check " +c.dateValue + '?=' + dt);
17014             if(c.dateValue == dt){
17015                 ret = c;
17016                 return false;
17017             }
17018             return true;
17019         });
17020         
17021         return ret;
17022     },
17023     
17024     findCells : function(ev) {
17025         var s = ev.start.clone().clearTime().getTime();
17026        // Roo.log(s);
17027         var e= ev.end.clone().clearTime().getTime();
17028        // Roo.log(e);
17029         var ret = [];
17030         this.cells.each(function(c){
17031              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17032             
17033             if(c.dateValue > e){
17034                 return ;
17035             }
17036             if(c.dateValue < s){
17037                 return ;
17038             }
17039             ret.push(c);
17040         });
17041         
17042         return ret;    
17043     },
17044     
17045 //    findBestRow: function(cells)
17046 //    {
17047 //        var ret = 0;
17048 //        
17049 //        for (var i =0 ; i < cells.length;i++) {
17050 //            ret  = Math.max(cells[i].rows || 0,ret);
17051 //        }
17052 //        return ret;
17053 //        
17054 //    },
17055     
17056     
17057     addItem : function(ev)
17058     {
17059         // look for vertical location slot in
17060         var cells = this.findCells(ev);
17061         
17062 //        ev.row = this.findBestRow(cells);
17063         
17064         // work out the location.
17065         
17066         var crow = false;
17067         var rows = [];
17068         for(var i =0; i < cells.length; i++) {
17069             
17070             cells[i].row = cells[0].row;
17071             
17072             if(i == 0){
17073                 cells[i].row = cells[i].row + 1;
17074             }
17075             
17076             if (!crow) {
17077                 crow = {
17078                     start : cells[i],
17079                     end :  cells[i]
17080                 };
17081                 continue;
17082             }
17083             if (crow.start.getY() == cells[i].getY()) {
17084                 // on same row.
17085                 crow.end = cells[i];
17086                 continue;
17087             }
17088             // different row.
17089             rows.push(crow);
17090             crow = {
17091                 start: cells[i],
17092                 end : cells[i]
17093             };
17094             
17095         }
17096         
17097         rows.push(crow);
17098         ev.els = [];
17099         ev.rows = rows;
17100         ev.cells = cells;
17101         
17102         cells[0].events.push(ev);
17103         
17104         this.calevents.push(ev);
17105     },
17106     
17107     clearEvents: function() {
17108         
17109         if(!this.calevents){
17110             return;
17111         }
17112         
17113         Roo.each(this.cells.elements, function(c){
17114             c.row = 0;
17115             c.events = [];
17116             c.more = [];
17117         });
17118         
17119         Roo.each(this.calevents, function(e) {
17120             Roo.each(e.els, function(el) {
17121                 el.un('mouseenter' ,this.onEventEnter, this);
17122                 el.un('mouseleave' ,this.onEventLeave, this);
17123                 el.remove();
17124             },this);
17125         },this);
17126         
17127         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17128             e.remove();
17129         });
17130         
17131     },
17132     
17133     renderEvents: function()
17134     {   
17135         var _this = this;
17136         
17137         this.cells.each(function(c) {
17138             
17139             if(c.row < 5){
17140                 return;
17141             }
17142             
17143             var ev = c.events;
17144             
17145             var r = 4;
17146             if(c.row != c.events.length){
17147                 r = 4 - (4 - (c.row - c.events.length));
17148             }
17149             
17150             c.events = ev.slice(0, r);
17151             c.more = ev.slice(r);
17152             
17153             if(c.more.length && c.more.length == 1){
17154                 c.events.push(c.more.pop());
17155             }
17156             
17157             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17158             
17159         });
17160             
17161         this.cells.each(function(c) {
17162             
17163             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17164             
17165             
17166             for (var e = 0; e < c.events.length; e++){
17167                 var ev = c.events[e];
17168                 var rows = ev.rows;
17169                 
17170                 for(var i = 0; i < rows.length; i++) {
17171                 
17172                     // how many rows should it span..
17173
17174                     var  cfg = {
17175                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17176                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17177
17178                         unselectable : "on",
17179                         cn : [
17180                             {
17181                                 cls: 'fc-event-inner',
17182                                 cn : [
17183     //                                {
17184     //                                  tag:'span',
17185     //                                  cls: 'fc-event-time',
17186     //                                  html : cells.length > 1 ? '' : ev.time
17187     //                                },
17188                                     {
17189                                       tag:'span',
17190                                       cls: 'fc-event-title',
17191                                       html : String.format('{0}', ev.title)
17192                                     }
17193
17194
17195                                 ]
17196                             },
17197                             {
17198                                 cls: 'ui-resizable-handle ui-resizable-e',
17199                                 html : '&nbsp;&nbsp;&nbsp'
17200                             }
17201
17202                         ]
17203                     };
17204
17205                     if (i == 0) {
17206                         cfg.cls += ' fc-event-start';
17207                     }
17208                     if ((i+1) == rows.length) {
17209                         cfg.cls += ' fc-event-end';
17210                     }
17211
17212                     var ctr = _this.el.select('.fc-event-container',true).first();
17213                     var cg = ctr.createChild(cfg);
17214
17215                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17216                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17217
17218                     var r = (c.more.length) ? 1 : 0;
17219                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17220                     cg.setWidth(ebox.right - sbox.x -2);
17221
17222                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17223                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17224                     cg.on('click', _this.onEventClick, _this, ev);
17225
17226                     ev.els.push(cg);
17227                     
17228                 }
17229                 
17230             }
17231             
17232             
17233             if(c.more.length){
17234                 var  cfg = {
17235                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17236                     style : 'position: absolute',
17237                     unselectable : "on",
17238                     cn : [
17239                         {
17240                             cls: 'fc-event-inner',
17241                             cn : [
17242                                 {
17243                                   tag:'span',
17244                                   cls: 'fc-event-title',
17245                                   html : 'More'
17246                                 }
17247
17248
17249                             ]
17250                         },
17251                         {
17252                             cls: 'ui-resizable-handle ui-resizable-e',
17253                             html : '&nbsp;&nbsp;&nbsp'
17254                         }
17255
17256                     ]
17257                 };
17258
17259                 var ctr = _this.el.select('.fc-event-container',true).first();
17260                 var cg = ctr.createChild(cfg);
17261
17262                 var sbox = c.select('.fc-day-content',true).first().getBox();
17263                 var ebox = c.select('.fc-day-content',true).first().getBox();
17264                 //Roo.log(cg);
17265                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17266                 cg.setWidth(ebox.right - sbox.x -2);
17267
17268                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17269                 
17270             }
17271             
17272         });
17273         
17274         
17275         
17276     },
17277     
17278     onEventEnter: function (e, el,event,d) {
17279         this.fireEvent('evententer', this, el, event);
17280     },
17281     
17282     onEventLeave: function (e, el,event,d) {
17283         this.fireEvent('eventleave', this, el, event);
17284     },
17285     
17286     onEventClick: function (e, el,event,d) {
17287         this.fireEvent('eventclick', this, el, event);
17288     },
17289     
17290     onMonthChange: function () {
17291         this.store.load();
17292     },
17293     
17294     onMoreEventClick: function(e, el, more)
17295     {
17296         var _this = this;
17297         
17298         this.calpopover.placement = 'right';
17299         this.calpopover.setTitle('More');
17300         
17301         this.calpopover.setContent('');
17302         
17303         var ctr = this.calpopover.el.select('.popover-content', true).first();
17304         
17305         Roo.each(more, function(m){
17306             var cfg = {
17307                 cls : 'fc-event-hori fc-event-draggable',
17308                 html : m.title
17309             };
17310             var cg = ctr.createChild(cfg);
17311             
17312             cg.on('click', _this.onEventClick, _this, m);
17313         });
17314         
17315         this.calpopover.show(el);
17316         
17317         
17318     },
17319     
17320     onLoad: function () 
17321     {   
17322         this.calevents = [];
17323         var cal = this;
17324         
17325         if(this.store.getCount() > 0){
17326             this.store.data.each(function(d){
17327                cal.addItem({
17328                     id : d.data.id,
17329                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17330                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17331                     time : d.data.start_time,
17332                     title : d.data.title,
17333                     description : d.data.description,
17334                     venue : d.data.venue
17335                 });
17336             });
17337         }
17338         
17339         this.renderEvents();
17340         
17341         if(this.calevents.length && this.loadMask){
17342             this.maskEl.hide();
17343         }
17344     },
17345     
17346     onBeforeLoad: function()
17347     {
17348         this.clearEvents();
17349         if(this.loadMask){
17350             this.maskEl.show();
17351         }
17352     }
17353 });
17354
17355  
17356  /*
17357  * - LGPL
17358  *
17359  * element
17360  * 
17361  */
17362
17363 /**
17364  * @class Roo.bootstrap.Popover
17365  * @extends Roo.bootstrap.Component
17366  * Bootstrap Popover class
17367  * @cfg {String} html contents of the popover   (or false to use children..)
17368  * @cfg {String} title of popover (or false to hide)
17369  * @cfg {String} placement how it is placed
17370  * @cfg {String} trigger click || hover (or false to trigger manually)
17371  * @cfg {String} over what (parent or false to trigger manually.)
17372  * @cfg {Number} delay - delay before showing
17373  
17374  * @constructor
17375  * Create a new Popover
17376  * @param {Object} config The config object
17377  */
17378
17379 Roo.bootstrap.Popover = function(config){
17380     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17381     
17382     this.addEvents({
17383         // raw events
17384          /**
17385          * @event show
17386          * After the popover show
17387          * 
17388          * @param {Roo.bootstrap.Popover} this
17389          */
17390         "show" : true,
17391         /**
17392          * @event hide
17393          * After the popover hide
17394          * 
17395          * @param {Roo.bootstrap.Popover} this
17396          */
17397         "hide" : true
17398     });
17399 };
17400
17401 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17402     
17403     title: 'Fill in a title',
17404     html: false,
17405     
17406     placement : 'right',
17407     trigger : 'hover', // hover
17408     
17409     delay : 0,
17410     
17411     over: 'parent',
17412     
17413     can_build_overlaid : false,
17414     
17415     getChildContainer : function()
17416     {
17417         return this.el.select('.popover-content',true).first();
17418     },
17419     
17420     getAutoCreate : function(){
17421          
17422         var cfg = {
17423            cls : 'popover roo-dynamic',
17424            style: 'display:block',
17425            cn : [
17426                 {
17427                     cls : 'arrow'
17428                 },
17429                 {
17430                     cls : 'popover-inner',
17431                     cn : [
17432                         {
17433                             tag: 'h3',
17434                             cls: 'popover-title',
17435                             html : this.title
17436                         },
17437                         {
17438                             cls : 'popover-content',
17439                             html : this.html
17440                         }
17441                     ]
17442                     
17443                 }
17444            ]
17445         };
17446         
17447         return cfg;
17448     },
17449     setTitle: function(str)
17450     {
17451         this.title = str;
17452         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17453     },
17454     setContent: function(str)
17455     {
17456         this.html = str;
17457         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17458     },
17459     // as it get's added to the bottom of the page.
17460     onRender : function(ct, position)
17461     {
17462         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17463         if(!this.el){
17464             var cfg = Roo.apply({},  this.getAutoCreate());
17465             cfg.id = Roo.id();
17466             
17467             if (this.cls) {
17468                 cfg.cls += ' ' + this.cls;
17469             }
17470             if (this.style) {
17471                 cfg.style = this.style;
17472             }
17473             //Roo.log("adding to ");
17474             this.el = Roo.get(document.body).createChild(cfg, position);
17475 //            Roo.log(this.el);
17476         }
17477         this.initEvents();
17478     },
17479     
17480     initEvents : function()
17481     {
17482         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17483         this.el.enableDisplayMode('block');
17484         this.el.hide();
17485         if (this.over === false) {
17486             return; 
17487         }
17488         if (this.triggers === false) {
17489             return;
17490         }
17491         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17492         var triggers = this.trigger ? this.trigger.split(' ') : [];
17493         Roo.each(triggers, function(trigger) {
17494         
17495             if (trigger == 'click') {
17496                 on_el.on('click', this.toggle, this);
17497             } else if (trigger != 'manual') {
17498                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17499                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17500       
17501                 on_el.on(eventIn  ,this.enter, this);
17502                 on_el.on(eventOut, this.leave, this);
17503             }
17504         }, this);
17505         
17506     },
17507     
17508     
17509     // private
17510     timeout : null,
17511     hoverState : null,
17512     
17513     toggle : function () {
17514         this.hoverState == 'in' ? this.leave() : this.enter();
17515     },
17516     
17517     enter : function () {
17518         
17519         clearTimeout(this.timeout);
17520     
17521         this.hoverState = 'in';
17522     
17523         if (!this.delay || !this.delay.show) {
17524             this.show();
17525             return;
17526         }
17527         var _t = this;
17528         this.timeout = setTimeout(function () {
17529             if (_t.hoverState == 'in') {
17530                 _t.show();
17531             }
17532         }, this.delay.show)
17533     },
17534     
17535     leave : function() {
17536         clearTimeout(this.timeout);
17537     
17538         this.hoverState = 'out';
17539     
17540         if (!this.delay || !this.delay.hide) {
17541             this.hide();
17542             return;
17543         }
17544         var _t = this;
17545         this.timeout = setTimeout(function () {
17546             if (_t.hoverState == 'out') {
17547                 _t.hide();
17548             }
17549         }, this.delay.hide)
17550     },
17551     
17552     show : function (on_el)
17553     {
17554         if (!on_el) {
17555             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17556         }
17557         
17558         // set content.
17559         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17560         if (this.html !== false) {
17561             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17562         }
17563         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17564         if (!this.title.length) {
17565             this.el.select('.popover-title',true).hide();
17566         }
17567         
17568         var placement = typeof this.placement == 'function' ?
17569             this.placement.call(this, this.el, on_el) :
17570             this.placement;
17571             
17572         var autoToken = /\s?auto?\s?/i;
17573         var autoPlace = autoToken.test(placement);
17574         if (autoPlace) {
17575             placement = placement.replace(autoToken, '') || 'top';
17576         }
17577         
17578         //this.el.detach()
17579         //this.el.setXY([0,0]);
17580         this.el.show();
17581         this.el.dom.style.display='block';
17582         this.el.addClass(placement);
17583         
17584         //this.el.appendTo(on_el);
17585         
17586         var p = this.getPosition();
17587         var box = this.el.getBox();
17588         
17589         if (autoPlace) {
17590             // fixme..
17591         }
17592         var align = Roo.bootstrap.Popover.alignment[placement];
17593         
17594 //        Roo.log(align);
17595         this.el.alignTo(on_el, align[0],align[1]);
17596         //var arrow = this.el.select('.arrow',true).first();
17597         //arrow.set(align[2], 
17598         
17599         this.el.addClass('in');
17600         
17601         
17602         if (this.el.hasClass('fade')) {
17603             // fade it?
17604         }
17605         
17606         this.hoverState = 'in';
17607         
17608         this.fireEvent('show', this);
17609         
17610     },
17611     hide : function()
17612     {
17613         this.el.setXY([0,0]);
17614         this.el.removeClass('in');
17615         this.el.hide();
17616         this.hoverState = null;
17617         
17618         this.fireEvent('hide', this);
17619     }
17620     
17621 });
17622
17623 Roo.bootstrap.Popover.alignment = {
17624     'left' : ['r-l', [-10,0], 'right'],
17625     'right' : ['l-r', [10,0], 'left'],
17626     'bottom' : ['t-b', [0,10], 'top'],
17627     'top' : [ 'b-t', [0,-10], 'bottom']
17628 };
17629
17630  /*
17631  * - LGPL
17632  *
17633  * Progress
17634  * 
17635  */
17636
17637 /**
17638  * @class Roo.bootstrap.Progress
17639  * @extends Roo.bootstrap.Component
17640  * Bootstrap Progress class
17641  * @cfg {Boolean} striped striped of the progress bar
17642  * @cfg {Boolean} active animated of the progress bar
17643  * 
17644  * 
17645  * @constructor
17646  * Create a new Progress
17647  * @param {Object} config The config object
17648  */
17649
17650 Roo.bootstrap.Progress = function(config){
17651     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17652 };
17653
17654 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17655     
17656     striped : false,
17657     active: false,
17658     
17659     getAutoCreate : function(){
17660         var cfg = {
17661             tag: 'div',
17662             cls: 'progress'
17663         };
17664         
17665         
17666         if(this.striped){
17667             cfg.cls += ' progress-striped';
17668         }
17669       
17670         if(this.active){
17671             cfg.cls += ' active';
17672         }
17673         
17674         
17675         return cfg;
17676     }
17677    
17678 });
17679
17680  
17681
17682  /*
17683  * - LGPL
17684  *
17685  * ProgressBar
17686  * 
17687  */
17688
17689 /**
17690  * @class Roo.bootstrap.ProgressBar
17691  * @extends Roo.bootstrap.Component
17692  * Bootstrap ProgressBar class
17693  * @cfg {Number} aria_valuenow aria-value now
17694  * @cfg {Number} aria_valuemin aria-value min
17695  * @cfg {Number} aria_valuemax aria-value max
17696  * @cfg {String} label label for the progress bar
17697  * @cfg {String} panel (success | info | warning | danger )
17698  * @cfg {String} role role of the progress bar
17699  * @cfg {String} sr_only text
17700  * 
17701  * 
17702  * @constructor
17703  * Create a new ProgressBar
17704  * @param {Object} config The config object
17705  */
17706
17707 Roo.bootstrap.ProgressBar = function(config){
17708     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17709 };
17710
17711 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17712     
17713     aria_valuenow : 0,
17714     aria_valuemin : 0,
17715     aria_valuemax : 100,
17716     label : false,
17717     panel : false,
17718     role : false,
17719     sr_only: false,
17720     
17721     getAutoCreate : function()
17722     {
17723         
17724         var cfg = {
17725             tag: 'div',
17726             cls: 'progress-bar',
17727             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17728         };
17729         
17730         if(this.sr_only){
17731             cfg.cn = {
17732                 tag: 'span',
17733                 cls: 'sr-only',
17734                 html: this.sr_only
17735             }
17736         }
17737         
17738         if(this.role){
17739             cfg.role = this.role;
17740         }
17741         
17742         if(this.aria_valuenow){
17743             cfg['aria-valuenow'] = this.aria_valuenow;
17744         }
17745         
17746         if(this.aria_valuemin){
17747             cfg['aria-valuemin'] = this.aria_valuemin;
17748         }
17749         
17750         if(this.aria_valuemax){
17751             cfg['aria-valuemax'] = this.aria_valuemax;
17752         }
17753         
17754         if(this.label && !this.sr_only){
17755             cfg.html = this.label;
17756         }
17757         
17758         if(this.panel){
17759             cfg.cls += ' progress-bar-' + this.panel;
17760         }
17761         
17762         return cfg;
17763     },
17764     
17765     update : function(aria_valuenow)
17766     {
17767         this.aria_valuenow = aria_valuenow;
17768         
17769         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17770     }
17771    
17772 });
17773
17774  
17775
17776  /*
17777  * - LGPL
17778  *
17779  * column
17780  * 
17781  */
17782
17783 /**
17784  * @class Roo.bootstrap.TabGroup
17785  * @extends Roo.bootstrap.Column
17786  * Bootstrap Column class
17787  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17788  * @cfg {Boolean} carousel true to make the group behave like a carousel
17789  * @cfg {Boolean} bullets show bullets for the panels
17790  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17791  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17792  * @cfg {Boolean} showarrow (true|false) show arrow default true
17793  * 
17794  * @constructor
17795  * Create a new TabGroup
17796  * @param {Object} config The config object
17797  */
17798
17799 Roo.bootstrap.TabGroup = function(config){
17800     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17801     if (!this.navId) {
17802         this.navId = Roo.id();
17803     }
17804     this.tabs = [];
17805     Roo.bootstrap.TabGroup.register(this);
17806     
17807 };
17808
17809 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17810     
17811     carousel : false,
17812     transition : false,
17813     bullets : 0,
17814     timer : 0,
17815     autoslide : false,
17816     slideFn : false,
17817     slideOnTouch : false,
17818     showarrow : true,
17819     
17820     getAutoCreate : function()
17821     {
17822         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17823         
17824         cfg.cls += ' tab-content';
17825         
17826         if (this.carousel) {
17827             cfg.cls += ' carousel slide';
17828             
17829             cfg.cn = [{
17830                cls : 'carousel-inner',
17831                cn : []
17832             }];
17833         
17834             if(this.bullets  && !Roo.isTouch){
17835                 
17836                 var bullets = {
17837                     cls : 'carousel-bullets',
17838                     cn : []
17839                 };
17840                
17841                 if(this.bullets_cls){
17842                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17843                 }
17844                 
17845                 bullets.cn.push({
17846                     cls : 'clear'
17847                 });
17848                 
17849                 cfg.cn[0].cn.push(bullets);
17850             }
17851             
17852             if(this.showarrow){
17853                 cfg.cn[0].cn.push({
17854                     tag : 'div',
17855                     class : 'carousel-arrow',
17856                     cn : [
17857                         {
17858                             tag : 'div',
17859                             class : 'carousel-prev',
17860                             cn : [
17861                                 {
17862                                     tag : 'i',
17863                                     class : 'fa fa-chevron-left'
17864                                 }
17865                             ]
17866                         },
17867                         {
17868                             tag : 'div',
17869                             class : 'carousel-next',
17870                             cn : [
17871                                 {
17872                                     tag : 'i',
17873                                     class : 'fa fa-chevron-right'
17874                                 }
17875                             ]
17876                         }
17877                     ]
17878                 });
17879             }
17880             
17881         }
17882         
17883         return cfg;
17884     },
17885     
17886     initEvents:  function()
17887     {
17888 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17889 //            this.el.on("touchstart", this.onTouchStart, this);
17890 //        }
17891         
17892         if(this.autoslide){
17893             var _this = this;
17894             
17895             this.slideFn = window.setInterval(function() {
17896                 _this.showPanelNext();
17897             }, this.timer);
17898         }
17899         
17900         if(this.showarrow){
17901             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17902             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17903         }
17904         
17905         
17906     },
17907     
17908 //    onTouchStart : function(e, el, o)
17909 //    {
17910 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17911 //            return;
17912 //        }
17913 //        
17914 //        this.showPanelNext();
17915 //    },
17916     
17917     
17918     getChildContainer : function()
17919     {
17920         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17921     },
17922     
17923     /**
17924     * register a Navigation item
17925     * @param {Roo.bootstrap.NavItem} the navitem to add
17926     */
17927     register : function(item)
17928     {
17929         this.tabs.push( item);
17930         item.navId = this.navId; // not really needed..
17931         this.addBullet();
17932     
17933     },
17934     
17935     getActivePanel : function()
17936     {
17937         var r = false;
17938         Roo.each(this.tabs, function(t) {
17939             if (t.active) {
17940                 r = t;
17941                 return false;
17942             }
17943             return null;
17944         });
17945         return r;
17946         
17947     },
17948     getPanelByName : function(n)
17949     {
17950         var r = false;
17951         Roo.each(this.tabs, function(t) {
17952             if (t.tabId == n) {
17953                 r = t;
17954                 return false;
17955             }
17956             return null;
17957         });
17958         return r;
17959     },
17960     indexOfPanel : function(p)
17961     {
17962         var r = false;
17963         Roo.each(this.tabs, function(t,i) {
17964             if (t.tabId == p.tabId) {
17965                 r = i;
17966                 return false;
17967             }
17968             return null;
17969         });
17970         return r;
17971     },
17972     /**
17973      * show a specific panel
17974      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17975      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17976      */
17977     showPanel : function (pan)
17978     {
17979         if(this.transition || typeof(pan) == 'undefined'){
17980             Roo.log("waiting for the transitionend");
17981             return;
17982         }
17983         
17984         if (typeof(pan) == 'number') {
17985             pan = this.tabs[pan];
17986         }
17987         
17988         if (typeof(pan) == 'string') {
17989             pan = this.getPanelByName(pan);
17990         }
17991         
17992         var cur = this.getActivePanel();
17993         
17994         if(!pan || !cur){
17995             Roo.log('pan or acitve pan is undefined');
17996             return false;
17997         }
17998         
17999         if (pan.tabId == this.getActivePanel().tabId) {
18000             return true;
18001         }
18002         
18003         if (false === cur.fireEvent('beforedeactivate')) {
18004             return false;
18005         }
18006         
18007         if(this.bullets > 0 && !Roo.isTouch){
18008             this.setActiveBullet(this.indexOfPanel(pan));
18009         }
18010         
18011         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18012             
18013             this.transition = true;
18014             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18015             var lr = dir == 'next' ? 'left' : 'right';
18016             pan.el.addClass(dir); // or prev
18017             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18018             cur.el.addClass(lr); // or right
18019             pan.el.addClass(lr);
18020             
18021             var _this = this;
18022             cur.el.on('transitionend', function() {
18023                 Roo.log("trans end?");
18024                 
18025                 pan.el.removeClass([lr,dir]);
18026                 pan.setActive(true);
18027                 
18028                 cur.el.removeClass([lr]);
18029                 cur.setActive(false);
18030                 
18031                 _this.transition = false;
18032                 
18033             }, this, { single:  true } );
18034             
18035             return true;
18036         }
18037         
18038         cur.setActive(false);
18039         pan.setActive(true);
18040         
18041         return true;
18042         
18043     },
18044     showPanelNext : function()
18045     {
18046         var i = this.indexOfPanel(this.getActivePanel());
18047         
18048         if (i >= this.tabs.length - 1 && !this.autoslide) {
18049             return;
18050         }
18051         
18052         if (i >= this.tabs.length - 1 && this.autoslide) {
18053             i = -1;
18054         }
18055         
18056         this.showPanel(this.tabs[i+1]);
18057     },
18058     
18059     showPanelPrev : function()
18060     {
18061         var i = this.indexOfPanel(this.getActivePanel());
18062         
18063         if (i  < 1 && !this.autoslide) {
18064             return;
18065         }
18066         
18067         if (i < 1 && this.autoslide) {
18068             i = this.tabs.length;
18069         }
18070         
18071         this.showPanel(this.tabs[i-1]);
18072     },
18073     
18074     
18075     addBullet: function()
18076     {
18077         if(!this.bullets || Roo.isTouch){
18078             return;
18079         }
18080         var ctr = this.el.select('.carousel-bullets',true).first();
18081         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18082         var bullet = ctr.createChild({
18083             cls : 'bullet bullet-' + i
18084         },ctr.dom.lastChild);
18085         
18086         
18087         var _this = this;
18088         
18089         bullet.on('click', (function(e, el, o, ii, t){
18090
18091             e.preventDefault();
18092
18093             this.showPanel(ii);
18094
18095             if(this.autoslide && this.slideFn){
18096                 clearInterval(this.slideFn);
18097                 this.slideFn = window.setInterval(function() {
18098                     _this.showPanelNext();
18099                 }, this.timer);
18100             }
18101
18102         }).createDelegate(this, [i, bullet], true));
18103                 
18104         
18105     },
18106      
18107     setActiveBullet : function(i)
18108     {
18109         if(Roo.isTouch){
18110             return;
18111         }
18112         
18113         Roo.each(this.el.select('.bullet', true).elements, function(el){
18114             el.removeClass('selected');
18115         });
18116
18117         var bullet = this.el.select('.bullet-' + i, true).first();
18118         
18119         if(!bullet){
18120             return;
18121         }
18122         
18123         bullet.addClass('selected');
18124     }
18125     
18126     
18127   
18128 });
18129
18130  
18131
18132  
18133  
18134 Roo.apply(Roo.bootstrap.TabGroup, {
18135     
18136     groups: {},
18137      /**
18138     * register a Navigation Group
18139     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18140     */
18141     register : function(navgrp)
18142     {
18143         this.groups[navgrp.navId] = navgrp;
18144         
18145     },
18146     /**
18147     * fetch a Navigation Group based on the navigation ID
18148     * if one does not exist , it will get created.
18149     * @param {string} the navgroup to add
18150     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18151     */
18152     get: function(navId) {
18153         if (typeof(this.groups[navId]) == 'undefined') {
18154             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18155         }
18156         return this.groups[navId] ;
18157     }
18158     
18159     
18160     
18161 });
18162
18163  /*
18164  * - LGPL
18165  *
18166  * TabPanel
18167  * 
18168  */
18169
18170 /**
18171  * @class Roo.bootstrap.TabPanel
18172  * @extends Roo.bootstrap.Component
18173  * Bootstrap TabPanel class
18174  * @cfg {Boolean} active panel active
18175  * @cfg {String} html panel content
18176  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18177  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18178  * @cfg {String} href click to link..
18179  * 
18180  * 
18181  * @constructor
18182  * Create a new TabPanel
18183  * @param {Object} config The config object
18184  */
18185
18186 Roo.bootstrap.TabPanel = function(config){
18187     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18188     this.addEvents({
18189         /**
18190              * @event changed
18191              * Fires when the active status changes
18192              * @param {Roo.bootstrap.TabPanel} this
18193              * @param {Boolean} state the new state
18194             
18195          */
18196         'changed': true,
18197         /**
18198              * @event beforedeactivate
18199              * Fires before a tab is de-activated - can be used to do validation on a form.
18200              * @param {Roo.bootstrap.TabPanel} this
18201              * @return {Boolean} false if there is an error
18202             
18203          */
18204         'beforedeactivate': true
18205      });
18206     
18207     this.tabId = this.tabId || Roo.id();
18208   
18209 };
18210
18211 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18212     
18213     active: false,
18214     html: false,
18215     tabId: false,
18216     navId : false,
18217     href : '',
18218     
18219     getAutoCreate : function(){
18220         var cfg = {
18221             tag: 'div',
18222             // item is needed for carousel - not sure if it has any effect otherwise
18223             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18224             html: this.html || ''
18225         };
18226         
18227         if(this.active){
18228             cfg.cls += ' active';
18229         }
18230         
18231         if(this.tabId){
18232             cfg.tabId = this.tabId;
18233         }
18234         
18235         
18236         return cfg;
18237     },
18238     
18239     initEvents:  function()
18240     {
18241         var p = this.parent();
18242         
18243         this.navId = this.navId || p.navId;
18244         
18245         if (typeof(this.navId) != 'undefined') {
18246             // not really needed.. but just in case.. parent should be a NavGroup.
18247             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18248             
18249             tg.register(this);
18250             
18251             var i = tg.tabs.length - 1;
18252             
18253             if(this.active && tg.bullets > 0 && i < tg.bullets){
18254                 tg.setActiveBullet(i);
18255             }
18256         }
18257         
18258         this.el.on('click', this.onClick, this);
18259         
18260         if(Roo.isTouch){
18261             this.el.on("touchstart", this.onTouchStart, this);
18262             this.el.on("touchmove", this.onTouchMove, this);
18263             this.el.on("touchend", this.onTouchEnd, this);
18264         }
18265         
18266     },
18267     
18268     onRender : function(ct, position)
18269     {
18270         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18271     },
18272     
18273     setActive : function(state)
18274     {
18275         Roo.log("panel - set active " + this.tabId + "=" + state);
18276         
18277         this.active = state;
18278         if (!state) {
18279             this.el.removeClass('active');
18280             
18281         } else  if (!this.el.hasClass('active')) {
18282             this.el.addClass('active');
18283         }
18284         
18285         this.fireEvent('changed', this, state);
18286     },
18287     
18288     onClick : function(e)
18289     {
18290         e.preventDefault();
18291         
18292         if(!this.href.length){
18293             return;
18294         }
18295         
18296         window.location.href = this.href;
18297     },
18298     
18299     startX : 0,
18300     startY : 0,
18301     endX : 0,
18302     endY : 0,
18303     swiping : false,
18304     
18305     onTouchStart : function(e)
18306     {
18307         this.swiping = false;
18308         
18309         this.startX = e.browserEvent.touches[0].clientX;
18310         this.startY = e.browserEvent.touches[0].clientY;
18311     },
18312     
18313     onTouchMove : function(e)
18314     {
18315         this.swiping = true;
18316         
18317         this.endX = e.browserEvent.touches[0].clientX;
18318         this.endY = e.browserEvent.touches[0].clientY;
18319     },
18320     
18321     onTouchEnd : function(e)
18322     {
18323         if(!this.swiping){
18324             this.onClick(e);
18325             return;
18326         }
18327         
18328         var tabGroup = this.parent();
18329         
18330         if(this.endX > this.startX){ // swiping right
18331             tabGroup.showPanelPrev();
18332             return;
18333         }
18334         
18335         if(this.startX > this.endX){ // swiping left
18336             tabGroup.showPanelNext();
18337             return;
18338         }
18339     }
18340     
18341     
18342 });
18343  
18344
18345  
18346
18347  /*
18348  * - LGPL
18349  *
18350  * DateField
18351  * 
18352  */
18353
18354 /**
18355  * @class Roo.bootstrap.DateField
18356  * @extends Roo.bootstrap.Input
18357  * Bootstrap DateField class
18358  * @cfg {Number} weekStart default 0
18359  * @cfg {String} viewMode default empty, (months|years)
18360  * @cfg {String} minViewMode default empty, (months|years)
18361  * @cfg {Number} startDate default -Infinity
18362  * @cfg {Number} endDate default Infinity
18363  * @cfg {Boolean} todayHighlight default false
18364  * @cfg {Boolean} todayBtn default false
18365  * @cfg {Boolean} calendarWeeks default false
18366  * @cfg {Object} daysOfWeekDisabled default empty
18367  * @cfg {Boolean} singleMode default false (true | false)
18368  * 
18369  * @cfg {Boolean} keyboardNavigation default true
18370  * @cfg {String} language default en
18371  * 
18372  * @constructor
18373  * Create a new DateField
18374  * @param {Object} config The config object
18375  */
18376
18377 Roo.bootstrap.DateField = function(config){
18378     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18379      this.addEvents({
18380             /**
18381              * @event show
18382              * Fires when this field show.
18383              * @param {Roo.bootstrap.DateField} this
18384              * @param {Mixed} date The date value
18385              */
18386             show : true,
18387             /**
18388              * @event show
18389              * Fires when this field hide.
18390              * @param {Roo.bootstrap.DateField} this
18391              * @param {Mixed} date The date value
18392              */
18393             hide : true,
18394             /**
18395              * @event select
18396              * Fires when select a date.
18397              * @param {Roo.bootstrap.DateField} this
18398              * @param {Mixed} date The date value
18399              */
18400             select : true,
18401             /**
18402              * @event beforeselect
18403              * Fires when before select a date.
18404              * @param {Roo.bootstrap.DateField} this
18405              * @param {Mixed} date The date value
18406              */
18407             beforeselect : true
18408         });
18409 };
18410
18411 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18412     
18413     /**
18414      * @cfg {String} format
18415      * The default date format string which can be overriden for localization support.  The format must be
18416      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18417      */
18418     format : "m/d/y",
18419     /**
18420      * @cfg {String} altFormats
18421      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18422      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18423      */
18424     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18425     
18426     weekStart : 0,
18427     
18428     viewMode : '',
18429     
18430     minViewMode : '',
18431     
18432     todayHighlight : false,
18433     
18434     todayBtn: false,
18435     
18436     language: 'en',
18437     
18438     keyboardNavigation: true,
18439     
18440     calendarWeeks: false,
18441     
18442     startDate: -Infinity,
18443     
18444     endDate: Infinity,
18445     
18446     daysOfWeekDisabled: [],
18447     
18448     _events: [],
18449     
18450     singleMode : false,
18451     
18452     UTCDate: function()
18453     {
18454         return new Date(Date.UTC.apply(Date, arguments));
18455     },
18456     
18457     UTCToday: function()
18458     {
18459         var today = new Date();
18460         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18461     },
18462     
18463     getDate: function() {
18464             var d = this.getUTCDate();
18465             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18466     },
18467     
18468     getUTCDate: function() {
18469             return this.date;
18470     },
18471     
18472     setDate: function(d) {
18473             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18474     },
18475     
18476     setUTCDate: function(d) {
18477             this.date = d;
18478             this.setValue(this.formatDate(this.date));
18479     },
18480         
18481     onRender: function(ct, position)
18482     {
18483         
18484         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18485         
18486         this.language = this.language || 'en';
18487         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18488         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18489         
18490         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18491         this.format = this.format || 'm/d/y';
18492         this.isInline = false;
18493         this.isInput = true;
18494         this.component = this.el.select('.add-on', true).first() || false;
18495         this.component = (this.component && this.component.length === 0) ? false : this.component;
18496         this.hasInput = this.component && this.inputEl().length;
18497         
18498         if (typeof(this.minViewMode === 'string')) {
18499             switch (this.minViewMode) {
18500                 case 'months':
18501                     this.minViewMode = 1;
18502                     break;
18503                 case 'years':
18504                     this.minViewMode = 2;
18505                     break;
18506                 default:
18507                     this.minViewMode = 0;
18508                     break;
18509             }
18510         }
18511         
18512         if (typeof(this.viewMode === 'string')) {
18513             switch (this.viewMode) {
18514                 case 'months':
18515                     this.viewMode = 1;
18516                     break;
18517                 case 'years':
18518                     this.viewMode = 2;
18519                     break;
18520                 default:
18521                     this.viewMode = 0;
18522                     break;
18523             }
18524         }
18525                 
18526         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18527         
18528 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18529         
18530         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18531         
18532         this.picker().on('mousedown', this.onMousedown, this);
18533         this.picker().on('click', this.onClick, this);
18534         
18535         this.picker().addClass('datepicker-dropdown');
18536         
18537         this.startViewMode = this.viewMode;
18538         
18539         if(this.singleMode){
18540             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18541                 v.setVisibilityMode(Roo.Element.DISPLAY);
18542                 v.hide();
18543             });
18544             
18545             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18546                 v.setStyle('width', '189px');
18547             });
18548         }
18549         
18550         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18551             if(!this.calendarWeeks){
18552                 v.remove();
18553                 return;
18554             }
18555             
18556             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18557             v.attr('colspan', function(i, val){
18558                 return parseInt(val) + 1;
18559             });
18560         });
18561                         
18562         
18563         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18564         
18565         this.setStartDate(this.startDate);
18566         this.setEndDate(this.endDate);
18567         
18568         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18569         
18570         this.fillDow();
18571         this.fillMonths();
18572         this.update();
18573         this.showMode();
18574         
18575         if(this.isInline) {
18576             this.showPopup();
18577         }
18578     },
18579     
18580     picker : function()
18581     {
18582         return this.pickerEl;
18583 //        return this.el.select('.datepicker', true).first();
18584     },
18585     
18586     fillDow: function()
18587     {
18588         var dowCnt = this.weekStart;
18589         
18590         var dow = {
18591             tag: 'tr',
18592             cn: [
18593                 
18594             ]
18595         };
18596         
18597         if(this.calendarWeeks){
18598             dow.cn.push({
18599                 tag: 'th',
18600                 cls: 'cw',
18601                 html: '&nbsp;'
18602             })
18603         }
18604         
18605         while (dowCnt < this.weekStart + 7) {
18606             dow.cn.push({
18607                 tag: 'th',
18608                 cls: 'dow',
18609                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18610             });
18611         }
18612         
18613         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18614     },
18615     
18616     fillMonths: function()
18617     {    
18618         var i = 0;
18619         var months = this.picker().select('>.datepicker-months td', true).first();
18620         
18621         months.dom.innerHTML = '';
18622         
18623         while (i < 12) {
18624             var month = {
18625                 tag: 'span',
18626                 cls: 'month',
18627                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18628             };
18629             
18630             months.createChild(month);
18631         }
18632         
18633     },
18634     
18635     update: function()
18636     {
18637         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;
18638         
18639         if (this.date < this.startDate) {
18640             this.viewDate = new Date(this.startDate);
18641         } else if (this.date > this.endDate) {
18642             this.viewDate = new Date(this.endDate);
18643         } else {
18644             this.viewDate = new Date(this.date);
18645         }
18646         
18647         this.fill();
18648     },
18649     
18650     fill: function() 
18651     {
18652         var d = new Date(this.viewDate),
18653                 year = d.getUTCFullYear(),
18654                 month = d.getUTCMonth(),
18655                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18656                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18657                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18658                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18659                 currentDate = this.date && this.date.valueOf(),
18660                 today = this.UTCToday();
18661         
18662         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18663         
18664 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18665         
18666 //        this.picker.select('>tfoot th.today').
18667 //                                              .text(dates[this.language].today)
18668 //                                              .toggle(this.todayBtn !== false);
18669     
18670         this.updateNavArrows();
18671         this.fillMonths();
18672                                                 
18673         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18674         
18675         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18676          
18677         prevMonth.setUTCDate(day);
18678         
18679         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18680         
18681         var nextMonth = new Date(prevMonth);
18682         
18683         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18684         
18685         nextMonth = nextMonth.valueOf();
18686         
18687         var fillMonths = false;
18688         
18689         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18690         
18691         while(prevMonth.valueOf() <= nextMonth) {
18692             var clsName = '';
18693             
18694             if (prevMonth.getUTCDay() === this.weekStart) {
18695                 if(fillMonths){
18696                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18697                 }
18698                     
18699                 fillMonths = {
18700                     tag: 'tr',
18701                     cn: []
18702                 };
18703                 
18704                 if(this.calendarWeeks){
18705                     // ISO 8601: First week contains first thursday.
18706                     // ISO also states week starts on Monday, but we can be more abstract here.
18707                     var
18708                     // Start of current week: based on weekstart/current date
18709                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18710                     // Thursday of this week
18711                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18712                     // First Thursday of year, year from thursday
18713                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18714                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18715                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18716                     
18717                     fillMonths.cn.push({
18718                         tag: 'td',
18719                         cls: 'cw',
18720                         html: calWeek
18721                     });
18722                 }
18723             }
18724             
18725             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18726                 clsName += ' old';
18727             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18728                 clsName += ' new';
18729             }
18730             if (this.todayHighlight &&
18731                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18732                 prevMonth.getUTCMonth() == today.getMonth() &&
18733                 prevMonth.getUTCDate() == today.getDate()) {
18734                 clsName += ' today';
18735             }
18736             
18737             if (currentDate && prevMonth.valueOf() === currentDate) {
18738                 clsName += ' active';
18739             }
18740             
18741             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18742                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18743                     clsName += ' disabled';
18744             }
18745             
18746             fillMonths.cn.push({
18747                 tag: 'td',
18748                 cls: 'day ' + clsName,
18749                 html: prevMonth.getDate()
18750             });
18751             
18752             prevMonth.setDate(prevMonth.getDate()+1);
18753         }
18754           
18755         var currentYear = this.date && this.date.getUTCFullYear();
18756         var currentMonth = this.date && this.date.getUTCMonth();
18757         
18758         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18759         
18760         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18761             v.removeClass('active');
18762             
18763             if(currentYear === year && k === currentMonth){
18764                 v.addClass('active');
18765             }
18766             
18767             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18768                 v.addClass('disabled');
18769             }
18770             
18771         });
18772         
18773         
18774         year = parseInt(year/10, 10) * 10;
18775         
18776         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18777         
18778         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18779         
18780         year -= 1;
18781         for (var i = -1; i < 11; i++) {
18782             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18783                 tag: 'span',
18784                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18785                 html: year
18786             });
18787             
18788             year += 1;
18789         }
18790     },
18791     
18792     showMode: function(dir) 
18793     {
18794         if (dir) {
18795             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18796         }
18797         
18798         Roo.each(this.picker().select('>div',true).elements, function(v){
18799             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18800             v.hide();
18801         });
18802         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18803     },
18804     
18805     place: function()
18806     {
18807         if(this.isInline) {
18808             return;
18809         }
18810         
18811         this.picker().removeClass(['bottom', 'top']);
18812         
18813         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18814             /*
18815              * place to the top of element!
18816              *
18817              */
18818             
18819             this.picker().addClass('top');
18820             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18821             
18822             return;
18823         }
18824         
18825         this.picker().addClass('bottom');
18826         
18827         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18828     },
18829     
18830     parseDate : function(value)
18831     {
18832         if(!value || value instanceof Date){
18833             return value;
18834         }
18835         var v = Date.parseDate(value, this.format);
18836         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18837             v = Date.parseDate(value, 'Y-m-d');
18838         }
18839         if(!v && this.altFormats){
18840             if(!this.altFormatsArray){
18841                 this.altFormatsArray = this.altFormats.split("|");
18842             }
18843             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18844                 v = Date.parseDate(value, this.altFormatsArray[i]);
18845             }
18846         }
18847         return v;
18848     },
18849     
18850     formatDate : function(date, fmt)
18851     {   
18852         return (!date || !(date instanceof Date)) ?
18853         date : date.dateFormat(fmt || this.format);
18854     },
18855     
18856     onFocus : function()
18857     {
18858         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18859         this.showPopup();
18860     },
18861     
18862     onBlur : function()
18863     {
18864         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18865         
18866         var d = this.inputEl().getValue();
18867         
18868         this.setValue(d);
18869                 
18870         this.hidePopup();
18871     },
18872     
18873     showPopup : function()
18874     {
18875         this.picker().show();
18876         this.update();
18877         this.place();
18878         
18879         this.fireEvent('showpopup', this, this.date);
18880     },
18881     
18882     hidePopup : function()
18883     {
18884         if(this.isInline) {
18885             return;
18886         }
18887         this.picker().hide();
18888         this.viewMode = this.startViewMode;
18889         this.showMode();
18890         
18891         this.fireEvent('hidepopup', this, this.date);
18892         
18893     },
18894     
18895     onMousedown: function(e)
18896     {
18897         e.stopPropagation();
18898         e.preventDefault();
18899     },
18900     
18901     keyup: function(e)
18902     {
18903         Roo.bootstrap.DateField.superclass.keyup.call(this);
18904         this.update();
18905     },
18906
18907     setValue: function(v)
18908     {
18909         if(this.fireEvent('beforeselect', this, v) !== false){
18910             var d = new Date(this.parseDate(v) ).clearTime();
18911         
18912             if(isNaN(d.getTime())){
18913                 this.date = this.viewDate = '';
18914                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18915                 return;
18916             }
18917
18918             v = this.formatDate(d);
18919
18920             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18921
18922             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18923
18924             this.update();
18925
18926             this.fireEvent('select', this, this.date);
18927         }
18928     },
18929     
18930     getValue: function()
18931     {
18932         return this.formatDate(this.date);
18933     },
18934     
18935     fireKey: function(e)
18936     {
18937         if (!this.picker().isVisible()){
18938             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18939                 this.showPopup();
18940             }
18941             return;
18942         }
18943         
18944         var dateChanged = false,
18945         dir, day, month,
18946         newDate, newViewDate;
18947         
18948         switch(e.keyCode){
18949             case 27: // escape
18950                 this.hidePopup();
18951                 e.preventDefault();
18952                 break;
18953             case 37: // left
18954             case 39: // right
18955                 if (!this.keyboardNavigation) {
18956                     break;
18957                 }
18958                 dir = e.keyCode == 37 ? -1 : 1;
18959                 
18960                 if (e.ctrlKey){
18961                     newDate = this.moveYear(this.date, dir);
18962                     newViewDate = this.moveYear(this.viewDate, dir);
18963                 } else if (e.shiftKey){
18964                     newDate = this.moveMonth(this.date, dir);
18965                     newViewDate = this.moveMonth(this.viewDate, dir);
18966                 } else {
18967                     newDate = new Date(this.date);
18968                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18969                     newViewDate = new Date(this.viewDate);
18970                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18971                 }
18972                 if (this.dateWithinRange(newDate)){
18973                     this.date = newDate;
18974                     this.viewDate = newViewDate;
18975                     this.setValue(this.formatDate(this.date));
18976 //                    this.update();
18977                     e.preventDefault();
18978                     dateChanged = true;
18979                 }
18980                 break;
18981             case 38: // up
18982             case 40: // down
18983                 if (!this.keyboardNavigation) {
18984                     break;
18985                 }
18986                 dir = e.keyCode == 38 ? -1 : 1;
18987                 if (e.ctrlKey){
18988                     newDate = this.moveYear(this.date, dir);
18989                     newViewDate = this.moveYear(this.viewDate, dir);
18990                 } else if (e.shiftKey){
18991                     newDate = this.moveMonth(this.date, dir);
18992                     newViewDate = this.moveMonth(this.viewDate, dir);
18993                 } else {
18994                     newDate = new Date(this.date);
18995                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18996                     newViewDate = new Date(this.viewDate);
18997                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18998                 }
18999                 if (this.dateWithinRange(newDate)){
19000                     this.date = newDate;
19001                     this.viewDate = newViewDate;
19002                     this.setValue(this.formatDate(this.date));
19003 //                    this.update();
19004                     e.preventDefault();
19005                     dateChanged = true;
19006                 }
19007                 break;
19008             case 13: // enter
19009                 this.setValue(this.formatDate(this.date));
19010                 this.hidePopup();
19011                 e.preventDefault();
19012                 break;
19013             case 9: // tab
19014                 this.setValue(this.formatDate(this.date));
19015                 this.hidePopup();
19016                 break;
19017             case 16: // shift
19018             case 17: // ctrl
19019             case 18: // alt
19020                 break;
19021             default :
19022                 this.hide();
19023                 
19024         }
19025     },
19026     
19027     
19028     onClick: function(e) 
19029     {
19030         e.stopPropagation();
19031         e.preventDefault();
19032         
19033         var target = e.getTarget();
19034         
19035         if(target.nodeName.toLowerCase() === 'i'){
19036             target = Roo.get(target).dom.parentNode;
19037         }
19038         
19039         var nodeName = target.nodeName;
19040         var className = target.className;
19041         var html = target.innerHTML;
19042         //Roo.log(nodeName);
19043         
19044         switch(nodeName.toLowerCase()) {
19045             case 'th':
19046                 switch(className) {
19047                     case 'switch':
19048                         this.showMode(1);
19049                         break;
19050                     case 'prev':
19051                     case 'next':
19052                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19053                         switch(this.viewMode){
19054                                 case 0:
19055                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19056                                         break;
19057                                 case 1:
19058                                 case 2:
19059                                         this.viewDate = this.moveYear(this.viewDate, dir);
19060                                         break;
19061                         }
19062                         this.fill();
19063                         break;
19064                     case 'today':
19065                         var date = new Date();
19066                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19067 //                        this.fill()
19068                         this.setValue(this.formatDate(this.date));
19069                         
19070                         this.hidePopup();
19071                         break;
19072                 }
19073                 break;
19074             case 'span':
19075                 if (className.indexOf('disabled') < 0) {
19076                     this.viewDate.setUTCDate(1);
19077                     if (className.indexOf('month') > -1) {
19078                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19079                     } else {
19080                         var year = parseInt(html, 10) || 0;
19081                         this.viewDate.setUTCFullYear(year);
19082                         
19083                     }
19084                     
19085                     if(this.singleMode){
19086                         this.setValue(this.formatDate(this.viewDate));
19087                         this.hidePopup();
19088                         return;
19089                     }
19090                     
19091                     this.showMode(-1);
19092                     this.fill();
19093                 }
19094                 break;
19095                 
19096             case 'td':
19097                 //Roo.log(className);
19098                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19099                     var day = parseInt(html, 10) || 1;
19100                     var year = this.viewDate.getUTCFullYear(),
19101                         month = this.viewDate.getUTCMonth();
19102
19103                     if (className.indexOf('old') > -1) {
19104                         if(month === 0 ){
19105                             month = 11;
19106                             year -= 1;
19107                         }else{
19108                             month -= 1;
19109                         }
19110                     } else if (className.indexOf('new') > -1) {
19111                         if (month == 11) {
19112                             month = 0;
19113                             year += 1;
19114                         } else {
19115                             month += 1;
19116                         }
19117                     }
19118                     //Roo.log([year,month,day]);
19119                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19120                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19121 //                    this.fill();
19122                     //Roo.log(this.formatDate(this.date));
19123                     this.setValue(this.formatDate(this.date));
19124                     this.hidePopup();
19125                 }
19126                 break;
19127         }
19128     },
19129     
19130     setStartDate: function(startDate)
19131     {
19132         this.startDate = startDate || -Infinity;
19133         if (this.startDate !== -Infinity) {
19134             this.startDate = this.parseDate(this.startDate);
19135         }
19136         this.update();
19137         this.updateNavArrows();
19138     },
19139
19140     setEndDate: function(endDate)
19141     {
19142         this.endDate = endDate || Infinity;
19143         if (this.endDate !== Infinity) {
19144             this.endDate = this.parseDate(this.endDate);
19145         }
19146         this.update();
19147         this.updateNavArrows();
19148     },
19149     
19150     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19151     {
19152         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19153         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19154             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19155         }
19156         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19157             return parseInt(d, 10);
19158         });
19159         this.update();
19160         this.updateNavArrows();
19161     },
19162     
19163     updateNavArrows: function() 
19164     {
19165         if(this.singleMode){
19166             return;
19167         }
19168         
19169         var d = new Date(this.viewDate),
19170         year = d.getUTCFullYear(),
19171         month = d.getUTCMonth();
19172         
19173         Roo.each(this.picker().select('.prev', true).elements, function(v){
19174             v.show();
19175             switch (this.viewMode) {
19176                 case 0:
19177
19178                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19179                         v.hide();
19180                     }
19181                     break;
19182                 case 1:
19183                 case 2:
19184                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19185                         v.hide();
19186                     }
19187                     break;
19188             }
19189         });
19190         
19191         Roo.each(this.picker().select('.next', true).elements, function(v){
19192             v.show();
19193             switch (this.viewMode) {
19194                 case 0:
19195
19196                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19197                         v.hide();
19198                     }
19199                     break;
19200                 case 1:
19201                 case 2:
19202                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19203                         v.hide();
19204                     }
19205                     break;
19206             }
19207         })
19208     },
19209     
19210     moveMonth: function(date, dir)
19211     {
19212         if (!dir) {
19213             return date;
19214         }
19215         var new_date = new Date(date.valueOf()),
19216         day = new_date.getUTCDate(),
19217         month = new_date.getUTCMonth(),
19218         mag = Math.abs(dir),
19219         new_month, test;
19220         dir = dir > 0 ? 1 : -1;
19221         if (mag == 1){
19222             test = dir == -1
19223             // If going back one month, make sure month is not current month
19224             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19225             ? function(){
19226                 return new_date.getUTCMonth() == month;
19227             }
19228             // If going forward one month, make sure month is as expected
19229             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19230             : function(){
19231                 return new_date.getUTCMonth() != new_month;
19232             };
19233             new_month = month + dir;
19234             new_date.setUTCMonth(new_month);
19235             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19236             if (new_month < 0 || new_month > 11) {
19237                 new_month = (new_month + 12) % 12;
19238             }
19239         } else {
19240             // For magnitudes >1, move one month at a time...
19241             for (var i=0; i<mag; i++) {
19242                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19243                 new_date = this.moveMonth(new_date, dir);
19244             }
19245             // ...then reset the day, keeping it in the new month
19246             new_month = new_date.getUTCMonth();
19247             new_date.setUTCDate(day);
19248             test = function(){
19249                 return new_month != new_date.getUTCMonth();
19250             };
19251         }
19252         // Common date-resetting loop -- if date is beyond end of month, make it
19253         // end of month
19254         while (test()){
19255             new_date.setUTCDate(--day);
19256             new_date.setUTCMonth(new_month);
19257         }
19258         return new_date;
19259     },
19260
19261     moveYear: function(date, dir)
19262     {
19263         return this.moveMonth(date, dir*12);
19264     },
19265
19266     dateWithinRange: function(date)
19267     {
19268         return date >= this.startDate && date <= this.endDate;
19269     },
19270
19271     
19272     remove: function() 
19273     {
19274         this.picker().remove();
19275     },
19276     
19277     validateValue : function(value)
19278     {
19279         if(this.getVisibilityEl().hasClass('hidden')){
19280             return true;
19281         }
19282         
19283         if(value.length < 1)  {
19284             if(this.allowBlank){
19285                 return true;
19286             }
19287             return false;
19288         }
19289         
19290         if(value.length < this.minLength){
19291             return false;
19292         }
19293         if(value.length > this.maxLength){
19294             return false;
19295         }
19296         if(this.vtype){
19297             var vt = Roo.form.VTypes;
19298             if(!vt[this.vtype](value, this)){
19299                 return false;
19300             }
19301         }
19302         if(typeof this.validator == "function"){
19303             var msg = this.validator(value);
19304             if(msg !== true){
19305                 return false;
19306             }
19307         }
19308         
19309         if(this.regex && !this.regex.test(value)){
19310             return false;
19311         }
19312         
19313         if(typeof(this.parseDate(value)) == 'undefined'){
19314             return false;
19315         }
19316         
19317         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19318             return false;
19319         }      
19320         
19321         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19322             return false;
19323         } 
19324         
19325         
19326         return true;
19327     },
19328     
19329     reset : function()
19330     {
19331         this.date = this.viewDate = '';
19332         
19333         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19334     }
19335    
19336 });
19337
19338 Roo.apply(Roo.bootstrap.DateField,  {
19339     
19340     head : {
19341         tag: 'thead',
19342         cn: [
19343         {
19344             tag: 'tr',
19345             cn: [
19346             {
19347                 tag: 'th',
19348                 cls: 'prev',
19349                 html: '<i class="fa fa-arrow-left"/>'
19350             },
19351             {
19352                 tag: 'th',
19353                 cls: 'switch',
19354                 colspan: '5'
19355             },
19356             {
19357                 tag: 'th',
19358                 cls: 'next',
19359                 html: '<i class="fa fa-arrow-right"/>'
19360             }
19361
19362             ]
19363         }
19364         ]
19365     },
19366     
19367     content : {
19368         tag: 'tbody',
19369         cn: [
19370         {
19371             tag: 'tr',
19372             cn: [
19373             {
19374                 tag: 'td',
19375                 colspan: '7'
19376             }
19377             ]
19378         }
19379         ]
19380     },
19381     
19382     footer : {
19383         tag: 'tfoot',
19384         cn: [
19385         {
19386             tag: 'tr',
19387             cn: [
19388             {
19389                 tag: 'th',
19390                 colspan: '7',
19391                 cls: 'today'
19392             }
19393                     
19394             ]
19395         }
19396         ]
19397     },
19398     
19399     dates:{
19400         en: {
19401             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19402             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19403             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19404             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19405             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19406             today: "Today"
19407         }
19408     },
19409     
19410     modes: [
19411     {
19412         clsName: 'days',
19413         navFnc: 'Month',
19414         navStep: 1
19415     },
19416     {
19417         clsName: 'months',
19418         navFnc: 'FullYear',
19419         navStep: 1
19420     },
19421     {
19422         clsName: 'years',
19423         navFnc: 'FullYear',
19424         navStep: 10
19425     }]
19426 });
19427
19428 Roo.apply(Roo.bootstrap.DateField,  {
19429   
19430     template : {
19431         tag: 'div',
19432         cls: 'datepicker dropdown-menu roo-dynamic',
19433         cn: [
19434         {
19435             tag: 'div',
19436             cls: 'datepicker-days',
19437             cn: [
19438             {
19439                 tag: 'table',
19440                 cls: 'table-condensed',
19441                 cn:[
19442                 Roo.bootstrap.DateField.head,
19443                 {
19444                     tag: 'tbody'
19445                 },
19446                 Roo.bootstrap.DateField.footer
19447                 ]
19448             }
19449             ]
19450         },
19451         {
19452             tag: 'div',
19453             cls: 'datepicker-months',
19454             cn: [
19455             {
19456                 tag: 'table',
19457                 cls: 'table-condensed',
19458                 cn:[
19459                 Roo.bootstrap.DateField.head,
19460                 Roo.bootstrap.DateField.content,
19461                 Roo.bootstrap.DateField.footer
19462                 ]
19463             }
19464             ]
19465         },
19466         {
19467             tag: 'div',
19468             cls: 'datepicker-years',
19469             cn: [
19470             {
19471                 tag: 'table',
19472                 cls: 'table-condensed',
19473                 cn:[
19474                 Roo.bootstrap.DateField.head,
19475                 Roo.bootstrap.DateField.content,
19476                 Roo.bootstrap.DateField.footer
19477                 ]
19478             }
19479             ]
19480         }
19481         ]
19482     }
19483 });
19484
19485  
19486
19487  /*
19488  * - LGPL
19489  *
19490  * TimeField
19491  * 
19492  */
19493
19494 /**
19495  * @class Roo.bootstrap.TimeField
19496  * @extends Roo.bootstrap.Input
19497  * Bootstrap DateField class
19498  * 
19499  * 
19500  * @constructor
19501  * Create a new TimeField
19502  * @param {Object} config The config object
19503  */
19504
19505 Roo.bootstrap.TimeField = function(config){
19506     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19507     this.addEvents({
19508             /**
19509              * @event show
19510              * Fires when this field show.
19511              * @param {Roo.bootstrap.DateField} thisthis
19512              * @param {Mixed} date The date value
19513              */
19514             show : true,
19515             /**
19516              * @event show
19517              * Fires when this field hide.
19518              * @param {Roo.bootstrap.DateField} this
19519              * @param {Mixed} date The date value
19520              */
19521             hide : true,
19522             /**
19523              * @event select
19524              * Fires when select a date.
19525              * @param {Roo.bootstrap.DateField} this
19526              * @param {Mixed} date The date value
19527              */
19528             select : true
19529         });
19530 };
19531
19532 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19533     
19534     /**
19535      * @cfg {String} format
19536      * The default time format string which can be overriden for localization support.  The format must be
19537      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19538      */
19539     format : "H:i",
19540        
19541     onRender: function(ct, position)
19542     {
19543         
19544         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19545                 
19546         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19547         
19548         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19549         
19550         this.pop = this.picker().select('>.datepicker-time',true).first();
19551         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19552         
19553         this.picker().on('mousedown', this.onMousedown, this);
19554         this.picker().on('click', this.onClick, this);
19555         
19556         this.picker().addClass('datepicker-dropdown');
19557     
19558         this.fillTime();
19559         this.update();
19560             
19561         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19562         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19563         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19564         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19565         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19566         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19567
19568     },
19569     
19570     fireKey: function(e){
19571         if (!this.picker().isVisible()){
19572             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19573                 this.show();
19574             }
19575             return;
19576         }
19577
19578         e.preventDefault();
19579         
19580         switch(e.keyCode){
19581             case 27: // escape
19582                 this.hide();
19583                 break;
19584             case 37: // left
19585             case 39: // right
19586                 this.onTogglePeriod();
19587                 break;
19588             case 38: // up
19589                 this.onIncrementMinutes();
19590                 break;
19591             case 40: // down
19592                 this.onDecrementMinutes();
19593                 break;
19594             case 13: // enter
19595             case 9: // tab
19596                 this.setTime();
19597                 break;
19598         }
19599     },
19600     
19601     onClick: function(e) {
19602         e.stopPropagation();
19603         e.preventDefault();
19604     },
19605     
19606     picker : function()
19607     {
19608         return this.el.select('.datepicker', true).first();
19609     },
19610     
19611     fillTime: function()
19612     {    
19613         var time = this.pop.select('tbody', true).first();
19614         
19615         time.dom.innerHTML = '';
19616         
19617         time.createChild({
19618             tag: 'tr',
19619             cn: [
19620                 {
19621                     tag: 'td',
19622                     cn: [
19623                         {
19624                             tag: 'a',
19625                             href: '#',
19626                             cls: 'btn',
19627                             cn: [
19628                                 {
19629                                     tag: 'span',
19630                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19631                                 }
19632                             ]
19633                         } 
19634                     ]
19635                 },
19636                 {
19637                     tag: 'td',
19638                     cls: 'separator'
19639                 },
19640                 {
19641                     tag: 'td',
19642                     cn: [
19643                         {
19644                             tag: 'a',
19645                             href: '#',
19646                             cls: 'btn',
19647                             cn: [
19648                                 {
19649                                     tag: 'span',
19650                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19651                                 }
19652                             ]
19653                         }
19654                     ]
19655                 },
19656                 {
19657                     tag: 'td',
19658                     cls: 'separator'
19659                 }
19660             ]
19661         });
19662         
19663         time.createChild({
19664             tag: 'tr',
19665             cn: [
19666                 {
19667                     tag: 'td',
19668                     cn: [
19669                         {
19670                             tag: 'span',
19671                             cls: 'timepicker-hour',
19672                             html: '00'
19673                         }  
19674                     ]
19675                 },
19676                 {
19677                     tag: 'td',
19678                     cls: 'separator',
19679                     html: ':'
19680                 },
19681                 {
19682                     tag: 'td',
19683                     cn: [
19684                         {
19685                             tag: 'span',
19686                             cls: 'timepicker-minute',
19687                             html: '00'
19688                         }  
19689                     ]
19690                 },
19691                 {
19692                     tag: 'td',
19693                     cls: 'separator'
19694                 },
19695                 {
19696                     tag: 'td',
19697                     cn: [
19698                         {
19699                             tag: 'button',
19700                             type: 'button',
19701                             cls: 'btn btn-primary period',
19702                             html: 'AM'
19703                             
19704                         }
19705                     ]
19706                 }
19707             ]
19708         });
19709         
19710         time.createChild({
19711             tag: 'tr',
19712             cn: [
19713                 {
19714                     tag: 'td',
19715                     cn: [
19716                         {
19717                             tag: 'a',
19718                             href: '#',
19719                             cls: 'btn',
19720                             cn: [
19721                                 {
19722                                     tag: 'span',
19723                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19724                                 }
19725                             ]
19726                         }
19727                     ]
19728                 },
19729                 {
19730                     tag: 'td',
19731                     cls: 'separator'
19732                 },
19733                 {
19734                     tag: 'td',
19735                     cn: [
19736                         {
19737                             tag: 'a',
19738                             href: '#',
19739                             cls: 'btn',
19740                             cn: [
19741                                 {
19742                                     tag: 'span',
19743                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19744                                 }
19745                             ]
19746                         }
19747                     ]
19748                 },
19749                 {
19750                     tag: 'td',
19751                     cls: 'separator'
19752                 }
19753             ]
19754         });
19755         
19756     },
19757     
19758     update: function()
19759     {
19760         
19761         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19762         
19763         this.fill();
19764     },
19765     
19766     fill: function() 
19767     {
19768         var hours = this.time.getHours();
19769         var minutes = this.time.getMinutes();
19770         var period = 'AM';
19771         
19772         if(hours > 11){
19773             period = 'PM';
19774         }
19775         
19776         if(hours == 0){
19777             hours = 12;
19778         }
19779         
19780         
19781         if(hours > 12){
19782             hours = hours - 12;
19783         }
19784         
19785         if(hours < 10){
19786             hours = '0' + hours;
19787         }
19788         
19789         if(minutes < 10){
19790             minutes = '0' + minutes;
19791         }
19792         
19793         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19794         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19795         this.pop.select('button', true).first().dom.innerHTML = period;
19796         
19797     },
19798     
19799     place: function()
19800     {   
19801         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19802         
19803         var cls = ['bottom'];
19804         
19805         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19806             cls.pop();
19807             cls.push('top');
19808         }
19809         
19810         cls.push('right');
19811         
19812         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19813             cls.pop();
19814             cls.push('left');
19815         }
19816         
19817         this.picker().addClass(cls.join('-'));
19818         
19819         var _this = this;
19820         
19821         Roo.each(cls, function(c){
19822             if(c == 'bottom'){
19823                 _this.picker().setTop(_this.inputEl().getHeight());
19824                 return;
19825             }
19826             if(c == 'top'){
19827                 _this.picker().setTop(0 - _this.picker().getHeight());
19828                 return;
19829             }
19830             
19831             if(c == 'left'){
19832                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19833                 return;
19834             }
19835             if(c == 'right'){
19836                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19837                 return;
19838             }
19839         });
19840         
19841     },
19842   
19843     onFocus : function()
19844     {
19845         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19846         this.show();
19847     },
19848     
19849     onBlur : function()
19850     {
19851         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19852         this.hide();
19853     },
19854     
19855     show : function()
19856     {
19857         this.picker().show();
19858         this.pop.show();
19859         this.update();
19860         this.place();
19861         
19862         this.fireEvent('show', this, this.date);
19863     },
19864     
19865     hide : function()
19866     {
19867         this.picker().hide();
19868         this.pop.hide();
19869         
19870         this.fireEvent('hide', this, this.date);
19871     },
19872     
19873     setTime : function()
19874     {
19875         this.hide();
19876         this.setValue(this.time.format(this.format));
19877         
19878         this.fireEvent('select', this, this.date);
19879         
19880         
19881     },
19882     
19883     onMousedown: function(e){
19884         e.stopPropagation();
19885         e.preventDefault();
19886     },
19887     
19888     onIncrementHours: function()
19889     {
19890         Roo.log('onIncrementHours');
19891         this.time = this.time.add(Date.HOUR, 1);
19892         this.update();
19893         
19894     },
19895     
19896     onDecrementHours: function()
19897     {
19898         Roo.log('onDecrementHours');
19899         this.time = this.time.add(Date.HOUR, -1);
19900         this.update();
19901     },
19902     
19903     onIncrementMinutes: function()
19904     {
19905         Roo.log('onIncrementMinutes');
19906         this.time = this.time.add(Date.MINUTE, 1);
19907         this.update();
19908     },
19909     
19910     onDecrementMinutes: function()
19911     {
19912         Roo.log('onDecrementMinutes');
19913         this.time = this.time.add(Date.MINUTE, -1);
19914         this.update();
19915     },
19916     
19917     onTogglePeriod: function()
19918     {
19919         Roo.log('onTogglePeriod');
19920         this.time = this.time.add(Date.HOUR, 12);
19921         this.update();
19922     }
19923     
19924    
19925 });
19926
19927 Roo.apply(Roo.bootstrap.TimeField,  {
19928     
19929     content : {
19930         tag: 'tbody',
19931         cn: [
19932             {
19933                 tag: 'tr',
19934                 cn: [
19935                 {
19936                     tag: 'td',
19937                     colspan: '7'
19938                 }
19939                 ]
19940             }
19941         ]
19942     },
19943     
19944     footer : {
19945         tag: 'tfoot',
19946         cn: [
19947             {
19948                 tag: 'tr',
19949                 cn: [
19950                 {
19951                     tag: 'th',
19952                     colspan: '7',
19953                     cls: '',
19954                     cn: [
19955                         {
19956                             tag: 'button',
19957                             cls: 'btn btn-info ok',
19958                             html: 'OK'
19959                         }
19960                     ]
19961                 }
19962
19963                 ]
19964             }
19965         ]
19966     }
19967 });
19968
19969 Roo.apply(Roo.bootstrap.TimeField,  {
19970   
19971     template : {
19972         tag: 'div',
19973         cls: 'datepicker dropdown-menu',
19974         cn: [
19975             {
19976                 tag: 'div',
19977                 cls: 'datepicker-time',
19978                 cn: [
19979                 {
19980                     tag: 'table',
19981                     cls: 'table-condensed',
19982                     cn:[
19983                     Roo.bootstrap.TimeField.content,
19984                     Roo.bootstrap.TimeField.footer
19985                     ]
19986                 }
19987                 ]
19988             }
19989         ]
19990     }
19991 });
19992
19993  
19994
19995  /*
19996  * - LGPL
19997  *
19998  * MonthField
19999  * 
20000  */
20001
20002 /**
20003  * @class Roo.bootstrap.MonthField
20004  * @extends Roo.bootstrap.Input
20005  * Bootstrap MonthField class
20006  * 
20007  * @cfg {String} language default en
20008  * 
20009  * @constructor
20010  * Create a new MonthField
20011  * @param {Object} config The config object
20012  */
20013
20014 Roo.bootstrap.MonthField = function(config){
20015     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20016     
20017     this.addEvents({
20018         /**
20019          * @event show
20020          * Fires when this field show.
20021          * @param {Roo.bootstrap.MonthField} this
20022          * @param {Mixed} date The date value
20023          */
20024         show : true,
20025         /**
20026          * @event show
20027          * Fires when this field hide.
20028          * @param {Roo.bootstrap.MonthField} this
20029          * @param {Mixed} date The date value
20030          */
20031         hide : true,
20032         /**
20033          * @event select
20034          * Fires when select a date.
20035          * @param {Roo.bootstrap.MonthField} this
20036          * @param {String} oldvalue The old value
20037          * @param {String} newvalue The new value
20038          */
20039         select : true
20040     });
20041 };
20042
20043 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20044     
20045     onRender: function(ct, position)
20046     {
20047         
20048         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20049         
20050         this.language = this.language || 'en';
20051         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20052         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20053         
20054         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20055         this.isInline = false;
20056         this.isInput = true;
20057         this.component = this.el.select('.add-on', true).first() || false;
20058         this.component = (this.component && this.component.length === 0) ? false : this.component;
20059         this.hasInput = this.component && this.inputEL().length;
20060         
20061         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20062         
20063         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20064         
20065         this.picker().on('mousedown', this.onMousedown, this);
20066         this.picker().on('click', this.onClick, this);
20067         
20068         this.picker().addClass('datepicker-dropdown');
20069         
20070         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20071             v.setStyle('width', '189px');
20072         });
20073         
20074         this.fillMonths();
20075         
20076         this.update();
20077         
20078         if(this.isInline) {
20079             this.show();
20080         }
20081         
20082     },
20083     
20084     setValue: function(v, suppressEvent)
20085     {   
20086         var o = this.getValue();
20087         
20088         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20089         
20090         this.update();
20091
20092         if(suppressEvent !== true){
20093             this.fireEvent('select', this, o, v);
20094         }
20095         
20096     },
20097     
20098     getValue: function()
20099     {
20100         return this.value;
20101     },
20102     
20103     onClick: function(e) 
20104     {
20105         e.stopPropagation();
20106         e.preventDefault();
20107         
20108         var target = e.getTarget();
20109         
20110         if(target.nodeName.toLowerCase() === 'i'){
20111             target = Roo.get(target).dom.parentNode;
20112         }
20113         
20114         var nodeName = target.nodeName;
20115         var className = target.className;
20116         var html = target.innerHTML;
20117         
20118         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20119             return;
20120         }
20121         
20122         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20123         
20124         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20125         
20126         this.hide();
20127                         
20128     },
20129     
20130     picker : function()
20131     {
20132         return this.pickerEl;
20133     },
20134     
20135     fillMonths: function()
20136     {    
20137         var i = 0;
20138         var months = this.picker().select('>.datepicker-months td', true).first();
20139         
20140         months.dom.innerHTML = '';
20141         
20142         while (i < 12) {
20143             var month = {
20144                 tag: 'span',
20145                 cls: 'month',
20146                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20147             };
20148             
20149             months.createChild(month);
20150         }
20151         
20152     },
20153     
20154     update: function()
20155     {
20156         var _this = this;
20157         
20158         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20159             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20160         }
20161         
20162         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20163             e.removeClass('active');
20164             
20165             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20166                 e.addClass('active');
20167             }
20168         })
20169     },
20170     
20171     place: function()
20172     {
20173         if(this.isInline) {
20174             return;
20175         }
20176         
20177         this.picker().removeClass(['bottom', 'top']);
20178         
20179         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20180             /*
20181              * place to the top of element!
20182              *
20183              */
20184             
20185             this.picker().addClass('top');
20186             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20187             
20188             return;
20189         }
20190         
20191         this.picker().addClass('bottom');
20192         
20193         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20194     },
20195     
20196     onFocus : function()
20197     {
20198         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20199         this.show();
20200     },
20201     
20202     onBlur : function()
20203     {
20204         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20205         
20206         var d = this.inputEl().getValue();
20207         
20208         this.setValue(d);
20209                 
20210         this.hide();
20211     },
20212     
20213     show : function()
20214     {
20215         this.picker().show();
20216         this.picker().select('>.datepicker-months', true).first().show();
20217         this.update();
20218         this.place();
20219         
20220         this.fireEvent('show', this, this.date);
20221     },
20222     
20223     hide : function()
20224     {
20225         if(this.isInline) {
20226             return;
20227         }
20228         this.picker().hide();
20229         this.fireEvent('hide', this, this.date);
20230         
20231     },
20232     
20233     onMousedown: function(e)
20234     {
20235         e.stopPropagation();
20236         e.preventDefault();
20237     },
20238     
20239     keyup: function(e)
20240     {
20241         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20242         this.update();
20243     },
20244
20245     fireKey: function(e)
20246     {
20247         if (!this.picker().isVisible()){
20248             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20249                 this.show();
20250             }
20251             return;
20252         }
20253         
20254         var dir;
20255         
20256         switch(e.keyCode){
20257             case 27: // escape
20258                 this.hide();
20259                 e.preventDefault();
20260                 break;
20261             case 37: // left
20262             case 39: // right
20263                 dir = e.keyCode == 37 ? -1 : 1;
20264                 
20265                 this.vIndex = this.vIndex + dir;
20266                 
20267                 if(this.vIndex < 0){
20268                     this.vIndex = 0;
20269                 }
20270                 
20271                 if(this.vIndex > 11){
20272                     this.vIndex = 11;
20273                 }
20274                 
20275                 if(isNaN(this.vIndex)){
20276                     this.vIndex = 0;
20277                 }
20278                 
20279                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20280                 
20281                 break;
20282             case 38: // up
20283             case 40: // down
20284                 
20285                 dir = e.keyCode == 38 ? -1 : 1;
20286                 
20287                 this.vIndex = this.vIndex + dir * 4;
20288                 
20289                 if(this.vIndex < 0){
20290                     this.vIndex = 0;
20291                 }
20292                 
20293                 if(this.vIndex > 11){
20294                     this.vIndex = 11;
20295                 }
20296                 
20297                 if(isNaN(this.vIndex)){
20298                     this.vIndex = 0;
20299                 }
20300                 
20301                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20302                 break;
20303                 
20304             case 13: // enter
20305                 
20306                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20307                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20308                 }
20309                 
20310                 this.hide();
20311                 e.preventDefault();
20312                 break;
20313             case 9: // tab
20314                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20315                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20316                 }
20317                 this.hide();
20318                 break;
20319             case 16: // shift
20320             case 17: // ctrl
20321             case 18: // alt
20322                 break;
20323             default :
20324                 this.hide();
20325                 
20326         }
20327     },
20328     
20329     remove: function() 
20330     {
20331         this.picker().remove();
20332     }
20333    
20334 });
20335
20336 Roo.apply(Roo.bootstrap.MonthField,  {
20337     
20338     content : {
20339         tag: 'tbody',
20340         cn: [
20341         {
20342             tag: 'tr',
20343             cn: [
20344             {
20345                 tag: 'td',
20346                 colspan: '7'
20347             }
20348             ]
20349         }
20350         ]
20351     },
20352     
20353     dates:{
20354         en: {
20355             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20356             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20357         }
20358     }
20359 });
20360
20361 Roo.apply(Roo.bootstrap.MonthField,  {
20362   
20363     template : {
20364         tag: 'div',
20365         cls: 'datepicker dropdown-menu roo-dynamic',
20366         cn: [
20367             {
20368                 tag: 'div',
20369                 cls: 'datepicker-months',
20370                 cn: [
20371                 {
20372                     tag: 'table',
20373                     cls: 'table-condensed',
20374                     cn:[
20375                         Roo.bootstrap.DateField.content
20376                     ]
20377                 }
20378                 ]
20379             }
20380         ]
20381     }
20382 });
20383
20384  
20385
20386  
20387  /*
20388  * - LGPL
20389  *
20390  * CheckBox
20391  * 
20392  */
20393
20394 /**
20395  * @class Roo.bootstrap.CheckBox
20396  * @extends Roo.bootstrap.Input
20397  * Bootstrap CheckBox class
20398  * 
20399  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20400  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20401  * @cfg {String} boxLabel The text that appears beside the checkbox
20402  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20403  * @cfg {Boolean} checked initnal the element
20404  * @cfg {Boolean} inline inline the element (default false)
20405  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20406  * @cfg {String} tooltip label tooltip
20407  * 
20408  * @constructor
20409  * Create a new CheckBox
20410  * @param {Object} config The config object
20411  */
20412
20413 Roo.bootstrap.CheckBox = function(config){
20414     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20415    
20416     this.addEvents({
20417         /**
20418         * @event check
20419         * Fires when the element is checked or unchecked.
20420         * @param {Roo.bootstrap.CheckBox} this This input
20421         * @param {Boolean} checked The new checked value
20422         */
20423        check : true,
20424        /**
20425         * @event click
20426         * Fires when the element is click.
20427         * @param {Roo.bootstrap.CheckBox} this This input
20428         */
20429        click : true
20430     });
20431     
20432 };
20433
20434 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20435   
20436     inputType: 'checkbox',
20437     inputValue: 1,
20438     valueOff: 0,
20439     boxLabel: false,
20440     checked: false,
20441     weight : false,
20442     inline: false,
20443     tooltip : '',
20444     
20445     getAutoCreate : function()
20446     {
20447         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20448         
20449         var id = Roo.id();
20450         
20451         var cfg = {};
20452         
20453         cfg.cls = 'form-group ' + this.inputType; //input-group
20454         
20455         if(this.inline){
20456             cfg.cls += ' ' + this.inputType + '-inline';
20457         }
20458         
20459         var input =  {
20460             tag: 'input',
20461             id : id,
20462             type : this.inputType,
20463             value : this.inputValue,
20464             cls : 'roo-' + this.inputType, //'form-box',
20465             placeholder : this.placeholder || ''
20466             
20467         };
20468         
20469         if(this.inputType != 'radio'){
20470             var hidden =  {
20471                 tag: 'input',
20472                 type : 'hidden',
20473                 cls : 'roo-hidden-value',
20474                 value : this.checked ? this.inputValue : this.valueOff
20475             };
20476         }
20477         
20478             
20479         if (this.weight) { // Validity check?
20480             cfg.cls += " " + this.inputType + "-" + this.weight;
20481         }
20482         
20483         if (this.disabled) {
20484             input.disabled=true;
20485         }
20486         
20487         if(this.checked){
20488             input.checked = this.checked;
20489         }
20490         
20491         if (this.name) {
20492             
20493             input.name = this.name;
20494             
20495             if(this.inputType != 'radio'){
20496                 hidden.name = this.name;
20497                 input.name = '_hidden_' + this.name;
20498             }
20499         }
20500         
20501         if (this.size) {
20502             input.cls += ' input-' + this.size;
20503         }
20504         
20505         var settings=this;
20506         
20507         ['xs','sm','md','lg'].map(function(size){
20508             if (settings[size]) {
20509                 cfg.cls += ' col-' + size + '-' + settings[size];
20510             }
20511         });
20512         
20513         var inputblock = input;
20514          
20515         if (this.before || this.after) {
20516             
20517             inputblock = {
20518                 cls : 'input-group',
20519                 cn :  [] 
20520             };
20521             
20522             if (this.before) {
20523                 inputblock.cn.push({
20524                     tag :'span',
20525                     cls : 'input-group-addon',
20526                     html : this.before
20527                 });
20528             }
20529             
20530             inputblock.cn.push(input);
20531             
20532             if(this.inputType != 'radio'){
20533                 inputblock.cn.push(hidden);
20534             }
20535             
20536             if (this.after) {
20537                 inputblock.cn.push({
20538                     tag :'span',
20539                     cls : 'input-group-addon',
20540                     html : this.after
20541                 });
20542             }
20543             
20544         }
20545         
20546         if (align ==='left' && this.fieldLabel.length) {
20547 //                Roo.log("left and has label");
20548             cfg.cn = [
20549                 {
20550                     tag: 'label',
20551                     'for' :  id,
20552                     cls : 'control-label',
20553                     html : this.fieldLabel
20554                 },
20555                 {
20556                     cls : "", 
20557                     cn: [
20558                         inputblock
20559                     ]
20560                 }
20561             ];
20562             
20563             if(this.labelWidth > 12){
20564                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20565             }
20566             
20567             if(this.labelWidth < 13 && this.labelmd == 0){
20568                 this.labelmd = this.labelWidth;
20569             }
20570             
20571             if(this.labellg > 0){
20572                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20573                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20574             }
20575             
20576             if(this.labelmd > 0){
20577                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20578                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20579             }
20580             
20581             if(this.labelsm > 0){
20582                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20583                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20584             }
20585             
20586             if(this.labelxs > 0){
20587                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20588                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20589             }
20590             
20591         } else if ( this.fieldLabel.length) {
20592 //                Roo.log(" label");
20593                 cfg.cn = [
20594                    
20595                     {
20596                         tag: this.boxLabel ? 'span' : 'label',
20597                         'for': id,
20598                         cls: 'control-label box-input-label',
20599                         //cls : 'input-group-addon',
20600                         html : this.fieldLabel
20601                     },
20602                     
20603                     inputblock
20604                     
20605                 ];
20606
20607         } else {
20608             
20609 //                Roo.log(" no label && no align");
20610                 cfg.cn = [  inputblock ] ;
20611                 
20612                 
20613         }
20614         
20615         if(this.boxLabel){
20616              var boxLabelCfg = {
20617                 tag: 'label',
20618                 //'for': id, // box label is handled by onclick - so no for...
20619                 cls: 'box-label',
20620                 html: this.boxLabel
20621             };
20622             
20623             if(this.tooltip){
20624                 boxLabelCfg.tooltip = this.tooltip;
20625             }
20626              
20627             cfg.cn.push(boxLabelCfg);
20628         }
20629         
20630         if(this.inputType != 'radio'){
20631             cfg.cn.push(hidden);
20632         }
20633         
20634         return cfg;
20635         
20636     },
20637     
20638     /**
20639      * return the real input element.
20640      */
20641     inputEl: function ()
20642     {
20643         return this.el.select('input.roo-' + this.inputType,true).first();
20644     },
20645     hiddenEl: function ()
20646     {
20647         return this.el.select('input.roo-hidden-value',true).first();
20648     },
20649     
20650     labelEl: function()
20651     {
20652         return this.el.select('label.control-label',true).first();
20653     },
20654     /* depricated... */
20655     
20656     label: function()
20657     {
20658         return this.labelEl();
20659     },
20660     
20661     boxLabelEl: function()
20662     {
20663         return this.el.select('label.box-label',true).first();
20664     },
20665     
20666     initEvents : function()
20667     {
20668 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20669         
20670         this.inputEl().on('click', this.onClick,  this);
20671         
20672         if (this.boxLabel) { 
20673             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20674         }
20675         
20676         this.startValue = this.getValue();
20677         
20678         if(this.groupId){
20679             Roo.bootstrap.CheckBox.register(this);
20680         }
20681     },
20682     
20683     onClick : function(e)
20684     {   
20685         if(this.fireEvent('click', this, e) !== false){
20686             this.setChecked(!this.checked);
20687         }
20688         
20689     },
20690     
20691     setChecked : function(state,suppressEvent)
20692     {
20693         this.startValue = this.getValue();
20694
20695         if(this.inputType == 'radio'){
20696             
20697             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20698                 e.dom.checked = false;
20699             });
20700             
20701             this.inputEl().dom.checked = true;
20702             
20703             this.inputEl().dom.value = this.inputValue;
20704             
20705             if(suppressEvent !== true){
20706                 this.fireEvent('check', this, true);
20707             }
20708             
20709             this.validate();
20710             
20711             return;
20712         }
20713         
20714         this.checked = state;
20715         
20716         this.inputEl().dom.checked = state;
20717         
20718         
20719         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20720         
20721         if(suppressEvent !== true){
20722             this.fireEvent('check', this, state);
20723         }
20724         
20725         this.validate();
20726     },
20727     
20728     getValue : function()
20729     {
20730         if(this.inputType == 'radio'){
20731             return this.getGroupValue();
20732         }
20733         
20734         return this.hiddenEl().dom.value;
20735         
20736     },
20737     
20738     getGroupValue : function()
20739     {
20740         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20741             return '';
20742         }
20743         
20744         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20745     },
20746     
20747     setValue : function(v,suppressEvent)
20748     {
20749         if(this.inputType == 'radio'){
20750             this.setGroupValue(v, suppressEvent);
20751             return;
20752         }
20753         
20754         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20755         
20756         this.validate();
20757     },
20758     
20759     setGroupValue : function(v, suppressEvent)
20760     {
20761         this.startValue = this.getValue();
20762         
20763         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20764             e.dom.checked = false;
20765             
20766             if(e.dom.value == v){
20767                 e.dom.checked = true;
20768             }
20769         });
20770         
20771         if(suppressEvent !== true){
20772             this.fireEvent('check', this, true);
20773         }
20774
20775         this.validate();
20776         
20777         return;
20778     },
20779     
20780     validate : function()
20781     {
20782         if(this.getVisibilityEl().hasClass('hidden')){
20783             return true;
20784         }
20785         
20786         if(
20787                 this.disabled || 
20788                 (this.inputType == 'radio' && this.validateRadio()) ||
20789                 (this.inputType == 'checkbox' && this.validateCheckbox())
20790         ){
20791             this.markValid();
20792             return true;
20793         }
20794         
20795         this.markInvalid();
20796         return false;
20797     },
20798     
20799     validateRadio : function()
20800     {
20801         if(this.getVisibilityEl().hasClass('hidden')){
20802             return true;
20803         }
20804         
20805         if(this.allowBlank){
20806             return true;
20807         }
20808         
20809         var valid = false;
20810         
20811         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20812             if(!e.dom.checked){
20813                 return;
20814             }
20815             
20816             valid = true;
20817             
20818             return false;
20819         });
20820         
20821         return valid;
20822     },
20823     
20824     validateCheckbox : function()
20825     {
20826         if(!this.groupId){
20827             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20828             //return (this.getValue() == this.inputValue) ? true : false;
20829         }
20830         
20831         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20832         
20833         if(!group){
20834             return false;
20835         }
20836         
20837         var r = false;
20838         
20839         for(var i in group){
20840             if(group[i].el.isVisible(true)){
20841                 r = false;
20842                 break;
20843             }
20844             
20845             r = true;
20846         }
20847         
20848         for(var i in group){
20849             if(r){
20850                 break;
20851             }
20852             
20853             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20854         }
20855         
20856         return r;
20857     },
20858     
20859     /**
20860      * Mark this field as valid
20861      */
20862     markValid : function()
20863     {
20864         var _this = this;
20865         
20866         this.fireEvent('valid', this);
20867         
20868         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20869         
20870         if(this.groupId){
20871             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20872         }
20873         
20874         if(label){
20875             label.markValid();
20876         }
20877
20878         if(this.inputType == 'radio'){
20879             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20880                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20881                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20882             });
20883             
20884             return;
20885         }
20886
20887         if(!this.groupId){
20888             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20889             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20890             return;
20891         }
20892         
20893         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20894         
20895         if(!group){
20896             return;
20897         }
20898         
20899         for(var i in group){
20900             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20901             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20902         }
20903     },
20904     
20905      /**
20906      * Mark this field as invalid
20907      * @param {String} msg The validation message
20908      */
20909     markInvalid : function(msg)
20910     {
20911         if(this.allowBlank){
20912             return;
20913         }
20914         
20915         var _this = this;
20916         
20917         this.fireEvent('invalid', this, msg);
20918         
20919         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20920         
20921         if(this.groupId){
20922             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20923         }
20924         
20925         if(label){
20926             label.markInvalid();
20927         }
20928             
20929         if(this.inputType == 'radio'){
20930             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20931                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20932                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20933             });
20934             
20935             return;
20936         }
20937         
20938         if(!this.groupId){
20939             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20940             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20941             return;
20942         }
20943         
20944         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20945         
20946         if(!group){
20947             return;
20948         }
20949         
20950         for(var i in group){
20951             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20952             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20953         }
20954         
20955     },
20956     
20957     clearInvalid : function()
20958     {
20959         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20960         
20961         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20962         
20963         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20964         
20965         if (label && label.iconEl) {
20966             label.iconEl.removeClass(label.validClass);
20967             label.iconEl.removeClass(label.invalidClass);
20968         }
20969     },
20970     
20971     disable : function()
20972     {
20973         if(this.inputType != 'radio'){
20974             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20975             return;
20976         }
20977         
20978         var _this = this;
20979         
20980         if(this.rendered){
20981             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20982                 _this.getActionEl().addClass(this.disabledClass);
20983                 e.dom.disabled = true;
20984             });
20985         }
20986         
20987         this.disabled = true;
20988         this.fireEvent("disable", this);
20989         return this;
20990     },
20991
20992     enable : function()
20993     {
20994         if(this.inputType != 'radio'){
20995             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20996             return;
20997         }
20998         
20999         var _this = this;
21000         
21001         if(this.rendered){
21002             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21003                 _this.getActionEl().removeClass(this.disabledClass);
21004                 e.dom.disabled = false;
21005             });
21006         }
21007         
21008         this.disabled = false;
21009         this.fireEvent("enable", this);
21010         return this;
21011     },
21012     
21013     setBoxLabel : function(v)
21014     {
21015         this.boxLabel = v;
21016         
21017         if(this.rendered){
21018             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21019         }
21020     }
21021
21022 });
21023
21024 Roo.apply(Roo.bootstrap.CheckBox, {
21025     
21026     groups: {},
21027     
21028      /**
21029     * register a CheckBox Group
21030     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21031     */
21032     register : function(checkbox)
21033     {
21034         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21035             this.groups[checkbox.groupId] = {};
21036         }
21037         
21038         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21039             return;
21040         }
21041         
21042         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21043         
21044     },
21045     /**
21046     * fetch a CheckBox Group based on the group ID
21047     * @param {string} the group ID
21048     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21049     */
21050     get: function(groupId) {
21051         if (typeof(this.groups[groupId]) == 'undefined') {
21052             return false;
21053         }
21054         
21055         return this.groups[groupId] ;
21056     }
21057     
21058     
21059 });
21060 /*
21061  * - LGPL
21062  *
21063  * RadioItem
21064  * 
21065  */
21066
21067 /**
21068  * @class Roo.bootstrap.Radio
21069  * @extends Roo.bootstrap.Component
21070  * Bootstrap Radio class
21071  * @cfg {String} boxLabel - the label associated
21072  * @cfg {String} value - the value of radio
21073  * 
21074  * @constructor
21075  * Create a new Radio
21076  * @param {Object} config The config object
21077  */
21078 Roo.bootstrap.Radio = function(config){
21079     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21080     
21081 };
21082
21083 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21084     
21085     boxLabel : '',
21086     
21087     value : '',
21088     
21089     getAutoCreate : function()
21090     {
21091         var cfg = {
21092             tag : 'div',
21093             cls : 'form-group radio',
21094             cn : [
21095                 {
21096                     tag : 'label',
21097                     cls : 'box-label',
21098                     html : this.boxLabel
21099                 }
21100             ]
21101         };
21102         
21103         return cfg;
21104     },
21105     
21106     initEvents : function() 
21107     {
21108         this.parent().register(this);
21109         
21110         this.el.on('click', this.onClick, this);
21111         
21112     },
21113     
21114     onClick : function(e)
21115     {
21116         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21117             this.setChecked(true);
21118         }
21119     },
21120     
21121     setChecked : function(state, suppressEvent)
21122     {
21123         this.parent().setValue(this.value, suppressEvent);
21124         
21125     },
21126     
21127     setBoxLabel : function(v)
21128     {
21129         this.boxLabel = v;
21130         
21131         if(this.rendered){
21132             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21133         }
21134     }
21135     
21136 });
21137  
21138
21139  /*
21140  * - LGPL
21141  *
21142  * Input
21143  * 
21144  */
21145
21146 /**
21147  * @class Roo.bootstrap.SecurePass
21148  * @extends Roo.bootstrap.Input
21149  * Bootstrap SecurePass class
21150  *
21151  * 
21152  * @constructor
21153  * Create a new SecurePass
21154  * @param {Object} config The config object
21155  */
21156  
21157 Roo.bootstrap.SecurePass = function (config) {
21158     // these go here, so the translation tool can replace them..
21159     this.errors = {
21160         PwdEmpty: "Please type a password, and then retype it to confirm.",
21161         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21162         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21163         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21164         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21165         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21166         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21167         TooWeak: "Your password is Too Weak."
21168     },
21169     this.meterLabel = "Password strength:";
21170     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21171     this.meterClass = [
21172         "roo-password-meter-tooweak", 
21173         "roo-password-meter-weak", 
21174         "roo-password-meter-medium", 
21175         "roo-password-meter-strong", 
21176         "roo-password-meter-grey"
21177     ];
21178     
21179     this.errors = {};
21180     
21181     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21182 }
21183
21184 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21185     /**
21186      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21187      * {
21188      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21189      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21190      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21191      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21192      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21193      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21194      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21195      * })
21196      */
21197     // private
21198     
21199     meterWidth: 300,
21200     errorMsg :'',    
21201     errors: false,
21202     imageRoot: '/',
21203     /**
21204      * @cfg {String/Object} Label for the strength meter (defaults to
21205      * 'Password strength:')
21206      */
21207     // private
21208     meterLabel: '',
21209     /**
21210      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21211      * ['Weak', 'Medium', 'Strong'])
21212      */
21213     // private    
21214     pwdStrengths: false,    
21215     // private
21216     strength: 0,
21217     // private
21218     _lastPwd: null,
21219     // private
21220     kCapitalLetter: 0,
21221     kSmallLetter: 1,
21222     kDigit: 2,
21223     kPunctuation: 3,
21224     
21225     insecure: false,
21226     // private
21227     initEvents: function ()
21228     {
21229         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21230
21231         if (this.el.is('input[type=password]') && Roo.isSafari) {
21232             this.el.on('keydown', this.SafariOnKeyDown, this);
21233         }
21234
21235         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21236     },
21237     // private
21238     onRender: function (ct, position)
21239     {
21240         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21241         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21242         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21243
21244         this.trigger.createChild({
21245                    cn: [
21246                     {
21247                     //id: 'PwdMeter',
21248                     tag: 'div',
21249                     cls: 'roo-password-meter-grey col-xs-12',
21250                     style: {
21251                         //width: 0,
21252                         //width: this.meterWidth + 'px'                                                
21253                         }
21254                     },
21255                     {                            
21256                          cls: 'roo-password-meter-text'                          
21257                     }
21258                 ]            
21259         });
21260
21261          
21262         if (this.hideTrigger) {
21263             this.trigger.setDisplayed(false);
21264         }
21265         this.setSize(this.width || '', this.height || '');
21266     },
21267     // private
21268     onDestroy: function ()
21269     {
21270         if (this.trigger) {
21271             this.trigger.removeAllListeners();
21272             this.trigger.remove();
21273         }
21274         if (this.wrap) {
21275             this.wrap.remove();
21276         }
21277         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21278     },
21279     // private
21280     checkStrength: function ()
21281     {
21282         var pwd = this.inputEl().getValue();
21283         if (pwd == this._lastPwd) {
21284             return;
21285         }
21286
21287         var strength;
21288         if (this.ClientSideStrongPassword(pwd)) {
21289             strength = 3;
21290         } else if (this.ClientSideMediumPassword(pwd)) {
21291             strength = 2;
21292         } else if (this.ClientSideWeakPassword(pwd)) {
21293             strength = 1;
21294         } else {
21295             strength = 0;
21296         }
21297         
21298         Roo.log('strength1: ' + strength);
21299         
21300         //var pm = this.trigger.child('div/div/div').dom;
21301         var pm = this.trigger.child('div/div');
21302         pm.removeClass(this.meterClass);
21303         pm.addClass(this.meterClass[strength]);
21304                 
21305         
21306         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21307                 
21308         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21309         
21310         this._lastPwd = pwd;
21311     },
21312     reset: function ()
21313     {
21314         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21315         
21316         this._lastPwd = '';
21317         
21318         var pm = this.trigger.child('div/div');
21319         pm.removeClass(this.meterClass);
21320         pm.addClass('roo-password-meter-grey');        
21321         
21322         
21323         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21324         
21325         pt.innerHTML = '';
21326         this.inputEl().dom.type='password';
21327     },
21328     // private
21329     validateValue: function (value)
21330     {
21331         
21332         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21333             return false;
21334         }
21335         if (value.length == 0) {
21336             if (this.allowBlank) {
21337                 this.clearInvalid();
21338                 return true;
21339             }
21340
21341             this.markInvalid(this.errors.PwdEmpty);
21342             this.errorMsg = this.errors.PwdEmpty;
21343             return false;
21344         }
21345         
21346         if(this.insecure){
21347             return true;
21348         }
21349         
21350         if ('[\x21-\x7e]*'.match(value)) {
21351             this.markInvalid(this.errors.PwdBadChar);
21352             this.errorMsg = this.errors.PwdBadChar;
21353             return false;
21354         }
21355         if (value.length < 6) {
21356             this.markInvalid(this.errors.PwdShort);
21357             this.errorMsg = this.errors.PwdShort;
21358             return false;
21359         }
21360         if (value.length > 16) {
21361             this.markInvalid(this.errors.PwdLong);
21362             this.errorMsg = this.errors.PwdLong;
21363             return false;
21364         }
21365         var strength;
21366         if (this.ClientSideStrongPassword(value)) {
21367             strength = 3;
21368         } else if (this.ClientSideMediumPassword(value)) {
21369             strength = 2;
21370         } else if (this.ClientSideWeakPassword(value)) {
21371             strength = 1;
21372         } else {
21373             strength = 0;
21374         }
21375
21376         
21377         if (strength < 2) {
21378             //this.markInvalid(this.errors.TooWeak);
21379             this.errorMsg = this.errors.TooWeak;
21380             //return false;
21381         }
21382         
21383         
21384         console.log('strength2: ' + strength);
21385         
21386         //var pm = this.trigger.child('div/div/div').dom;
21387         
21388         var pm = this.trigger.child('div/div');
21389         pm.removeClass(this.meterClass);
21390         pm.addClass(this.meterClass[strength]);
21391                 
21392         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21393                 
21394         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21395         
21396         this.errorMsg = ''; 
21397         return true;
21398     },
21399     // private
21400     CharacterSetChecks: function (type)
21401     {
21402         this.type = type;
21403         this.fResult = false;
21404     },
21405     // private
21406     isctype: function (character, type)
21407     {
21408         switch (type) {  
21409             case this.kCapitalLetter:
21410                 if (character >= 'A' && character <= 'Z') {
21411                     return true;
21412                 }
21413                 break;
21414             
21415             case this.kSmallLetter:
21416                 if (character >= 'a' && character <= 'z') {
21417                     return true;
21418                 }
21419                 break;
21420             
21421             case this.kDigit:
21422                 if (character >= '0' && character <= '9') {
21423                     return true;
21424                 }
21425                 break;
21426             
21427             case this.kPunctuation:
21428                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21429                     return true;
21430                 }
21431                 break;
21432             
21433             default:
21434                 return false;
21435         }
21436
21437     },
21438     // private
21439     IsLongEnough: function (pwd, size)
21440     {
21441         return !(pwd == null || isNaN(size) || pwd.length < size);
21442     },
21443     // private
21444     SpansEnoughCharacterSets: function (word, nb)
21445     {
21446         if (!this.IsLongEnough(word, nb))
21447         {
21448             return false;
21449         }
21450
21451         var characterSetChecks = new Array(
21452             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21453             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21454         );
21455         
21456         for (var index = 0; index < word.length; ++index) {
21457             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21458                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21459                     characterSetChecks[nCharSet].fResult = true;
21460                     break;
21461                 }
21462             }
21463         }
21464
21465         var nCharSets = 0;
21466         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21467             if (characterSetChecks[nCharSet].fResult) {
21468                 ++nCharSets;
21469             }
21470         }
21471
21472         if (nCharSets < nb) {
21473             return false;
21474         }
21475         return true;
21476     },
21477     // private
21478     ClientSideStrongPassword: function (pwd)
21479     {
21480         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21481     },
21482     // private
21483     ClientSideMediumPassword: function (pwd)
21484     {
21485         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21486     },
21487     // private
21488     ClientSideWeakPassword: function (pwd)
21489     {
21490         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21491     }
21492           
21493 })//<script type="text/javascript">
21494
21495 /*
21496  * Based  Ext JS Library 1.1.1
21497  * Copyright(c) 2006-2007, Ext JS, LLC.
21498  * LGPL
21499  *
21500  */
21501  
21502 /**
21503  * @class Roo.HtmlEditorCore
21504  * @extends Roo.Component
21505  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21506  *
21507  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21508  */
21509
21510 Roo.HtmlEditorCore = function(config){
21511     
21512     
21513     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21514     
21515     
21516     this.addEvents({
21517         /**
21518          * @event initialize
21519          * Fires when the editor is fully initialized (including the iframe)
21520          * @param {Roo.HtmlEditorCore} this
21521          */
21522         initialize: true,
21523         /**
21524          * @event activate
21525          * Fires when the editor is first receives the focus. Any insertion must wait
21526          * until after this event.
21527          * @param {Roo.HtmlEditorCore} this
21528          */
21529         activate: true,
21530          /**
21531          * @event beforesync
21532          * Fires before the textarea is updated with content from the editor iframe. Return false
21533          * to cancel the sync.
21534          * @param {Roo.HtmlEditorCore} this
21535          * @param {String} html
21536          */
21537         beforesync: true,
21538          /**
21539          * @event beforepush
21540          * Fires before the iframe editor is updated with content from the textarea. Return false
21541          * to cancel the push.
21542          * @param {Roo.HtmlEditorCore} this
21543          * @param {String} html
21544          */
21545         beforepush: true,
21546          /**
21547          * @event sync
21548          * Fires when the textarea is updated with content from the editor iframe.
21549          * @param {Roo.HtmlEditorCore} this
21550          * @param {String} html
21551          */
21552         sync: true,
21553          /**
21554          * @event push
21555          * Fires when the iframe editor is updated with content from the textarea.
21556          * @param {Roo.HtmlEditorCore} this
21557          * @param {String} html
21558          */
21559         push: true,
21560         
21561         /**
21562          * @event editorevent
21563          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21564          * @param {Roo.HtmlEditorCore} this
21565          */
21566         editorevent: true
21567         
21568     });
21569     
21570     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21571     
21572     // defaults : white / black...
21573     this.applyBlacklists();
21574     
21575     
21576     
21577 };
21578
21579
21580 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21581
21582
21583      /**
21584      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21585      */
21586     
21587     owner : false,
21588     
21589      /**
21590      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21591      *                        Roo.resizable.
21592      */
21593     resizable : false,
21594      /**
21595      * @cfg {Number} height (in pixels)
21596      */   
21597     height: 300,
21598    /**
21599      * @cfg {Number} width (in pixels)
21600      */   
21601     width: 500,
21602     
21603     /**
21604      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21605      * 
21606      */
21607     stylesheets: false,
21608     
21609     // id of frame..
21610     frameId: false,
21611     
21612     // private properties
21613     validationEvent : false,
21614     deferHeight: true,
21615     initialized : false,
21616     activated : false,
21617     sourceEditMode : false,
21618     onFocus : Roo.emptyFn,
21619     iframePad:3,
21620     hideMode:'offsets',
21621     
21622     clearUp: true,
21623     
21624     // blacklist + whitelisted elements..
21625     black: false,
21626     white: false,
21627      
21628     bodyCls : '',
21629
21630     /**
21631      * Protected method that will not generally be called directly. It
21632      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21633      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21634      */
21635     getDocMarkup : function(){
21636         // body styles..
21637         var st = '';
21638         
21639         // inherit styels from page...?? 
21640         if (this.stylesheets === false) {
21641             
21642             Roo.get(document.head).select('style').each(function(node) {
21643                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21644             });
21645             
21646             Roo.get(document.head).select('link').each(function(node) { 
21647                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21648             });
21649             
21650         } else if (!this.stylesheets.length) {
21651                 // simple..
21652                 st = '<style type="text/css">' +
21653                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21654                    '</style>';
21655         } else { 
21656             st = '<style type="text/css">' +
21657                     this.stylesheets +
21658                 '</style>';
21659         }
21660         
21661         st +=  '<style type="text/css">' +
21662             'IMG { cursor: pointer } ' +
21663         '</style>';
21664
21665         var cls = 'roo-htmleditor-body';
21666         
21667         if(this.bodyCls.length){
21668             cls += ' ' + this.bodyCls;
21669         }
21670         
21671         return '<html><head>' + st  +
21672             //<style type="text/css">' +
21673             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21674             //'</style>' +
21675             ' </head><body class="' +  cls + '"></body></html>';
21676     },
21677
21678     // private
21679     onRender : function(ct, position)
21680     {
21681         var _t = this;
21682         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21683         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21684         
21685         
21686         this.el.dom.style.border = '0 none';
21687         this.el.dom.setAttribute('tabIndex', -1);
21688         this.el.addClass('x-hidden hide');
21689         
21690         
21691         
21692         if(Roo.isIE){ // fix IE 1px bogus margin
21693             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21694         }
21695        
21696         
21697         this.frameId = Roo.id();
21698         
21699          
21700         
21701         var iframe = this.owner.wrap.createChild({
21702             tag: 'iframe',
21703             cls: 'form-control', // bootstrap..
21704             id: this.frameId,
21705             name: this.frameId,
21706             frameBorder : 'no',
21707             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21708         }, this.el
21709         );
21710         
21711         
21712         this.iframe = iframe.dom;
21713
21714          this.assignDocWin();
21715         
21716         this.doc.designMode = 'on';
21717        
21718         this.doc.open();
21719         this.doc.write(this.getDocMarkup());
21720         this.doc.close();
21721
21722         
21723         var task = { // must defer to wait for browser to be ready
21724             run : function(){
21725                 //console.log("run task?" + this.doc.readyState);
21726                 this.assignDocWin();
21727                 if(this.doc.body || this.doc.readyState == 'complete'){
21728                     try {
21729                         this.doc.designMode="on";
21730                     } catch (e) {
21731                         return;
21732                     }
21733                     Roo.TaskMgr.stop(task);
21734                     this.initEditor.defer(10, this);
21735                 }
21736             },
21737             interval : 10,
21738             duration: 10000,
21739             scope: this
21740         };
21741         Roo.TaskMgr.start(task);
21742
21743     },
21744
21745     // private
21746     onResize : function(w, h)
21747     {
21748          Roo.log('resize: ' +w + ',' + h );
21749         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21750         if(!this.iframe){
21751             return;
21752         }
21753         if(typeof w == 'number'){
21754             
21755             this.iframe.style.width = w + 'px';
21756         }
21757         if(typeof h == 'number'){
21758             
21759             this.iframe.style.height = h + 'px';
21760             if(this.doc){
21761                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21762             }
21763         }
21764         
21765     },
21766
21767     /**
21768      * Toggles the editor between standard and source edit mode.
21769      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21770      */
21771     toggleSourceEdit : function(sourceEditMode){
21772         
21773         this.sourceEditMode = sourceEditMode === true;
21774         
21775         if(this.sourceEditMode){
21776  
21777             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21778             
21779         }else{
21780             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21781             //this.iframe.className = '';
21782             this.deferFocus();
21783         }
21784         //this.setSize(this.owner.wrap.getSize());
21785         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21786     },
21787
21788     
21789   
21790
21791     /**
21792      * Protected method that will not generally be called directly. If you need/want
21793      * custom HTML cleanup, this is the method you should override.
21794      * @param {String} html The HTML to be cleaned
21795      * return {String} The cleaned HTML
21796      */
21797     cleanHtml : function(html){
21798         html = String(html);
21799         if(html.length > 5){
21800             if(Roo.isSafari){ // strip safari nonsense
21801                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21802             }
21803         }
21804         if(html == '&nbsp;'){
21805             html = '';
21806         }
21807         return html;
21808     },
21809
21810     /**
21811      * HTML Editor -> Textarea
21812      * Protected method that will not generally be called directly. Syncs the contents
21813      * of the editor iframe with the textarea.
21814      */
21815     syncValue : function(){
21816         if(this.initialized){
21817             var bd = (this.doc.body || this.doc.documentElement);
21818             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21819             var html = bd.innerHTML;
21820             if(Roo.isSafari){
21821                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21822                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21823                 if(m && m[1]){
21824                     html = '<div style="'+m[0]+'">' + html + '</div>';
21825                 }
21826             }
21827             html = this.cleanHtml(html);
21828             // fix up the special chars.. normaly like back quotes in word...
21829             // however we do not want to do this with chinese..
21830             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21831                 var cc = b.charCodeAt();
21832                 if (
21833                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21834                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21835                     (cc >= 0xf900 && cc < 0xfb00 )
21836                 ) {
21837                         return b;
21838                 }
21839                 return "&#"+cc+";" 
21840             });
21841             if(this.owner.fireEvent('beforesync', this, html) !== false){
21842                 this.el.dom.value = html;
21843                 this.owner.fireEvent('sync', this, html);
21844             }
21845         }
21846     },
21847
21848     /**
21849      * Protected method that will not generally be called directly. Pushes the value of the textarea
21850      * into the iframe editor.
21851      */
21852     pushValue : function(){
21853         if(this.initialized){
21854             var v = this.el.dom.value.trim();
21855             
21856 //            if(v.length < 1){
21857 //                v = '&#160;';
21858 //            }
21859             
21860             if(this.owner.fireEvent('beforepush', this, v) !== false){
21861                 var d = (this.doc.body || this.doc.documentElement);
21862                 d.innerHTML = v;
21863                 this.cleanUpPaste();
21864                 this.el.dom.value = d.innerHTML;
21865                 this.owner.fireEvent('push', this, v);
21866             }
21867         }
21868     },
21869
21870     // private
21871     deferFocus : function(){
21872         this.focus.defer(10, this);
21873     },
21874
21875     // doc'ed in Field
21876     focus : function(){
21877         if(this.win && !this.sourceEditMode){
21878             this.win.focus();
21879         }else{
21880             this.el.focus();
21881         }
21882     },
21883     
21884     assignDocWin: function()
21885     {
21886         var iframe = this.iframe;
21887         
21888          if(Roo.isIE){
21889             this.doc = iframe.contentWindow.document;
21890             this.win = iframe.contentWindow;
21891         } else {
21892 //            if (!Roo.get(this.frameId)) {
21893 //                return;
21894 //            }
21895 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21896 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21897             
21898             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21899                 return;
21900             }
21901             
21902             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21903             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21904         }
21905     },
21906     
21907     // private
21908     initEditor : function(){
21909         //console.log("INIT EDITOR");
21910         this.assignDocWin();
21911         
21912         
21913         
21914         this.doc.designMode="on";
21915         this.doc.open();
21916         this.doc.write(this.getDocMarkup());
21917         this.doc.close();
21918         
21919         var dbody = (this.doc.body || this.doc.documentElement);
21920         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21921         // this copies styles from the containing element into thsi one..
21922         // not sure why we need all of this..
21923         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21924         
21925         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21926         //ss['background-attachment'] = 'fixed'; // w3c
21927         dbody.bgProperties = 'fixed'; // ie
21928         //Roo.DomHelper.applyStyles(dbody, ss);
21929         Roo.EventManager.on(this.doc, {
21930             //'mousedown': this.onEditorEvent,
21931             'mouseup': this.onEditorEvent,
21932             'dblclick': this.onEditorEvent,
21933             'click': this.onEditorEvent,
21934             'keyup': this.onEditorEvent,
21935             buffer:100,
21936             scope: this
21937         });
21938         if(Roo.isGecko){
21939             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21940         }
21941         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21942             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21943         }
21944         this.initialized = true;
21945
21946         this.owner.fireEvent('initialize', this);
21947         this.pushValue();
21948     },
21949
21950     // private
21951     onDestroy : function(){
21952         
21953         
21954         
21955         if(this.rendered){
21956             
21957             //for (var i =0; i < this.toolbars.length;i++) {
21958             //    // fixme - ask toolbars for heights?
21959             //    this.toolbars[i].onDestroy();
21960            // }
21961             
21962             //this.wrap.dom.innerHTML = '';
21963             //this.wrap.remove();
21964         }
21965     },
21966
21967     // private
21968     onFirstFocus : function(){
21969         
21970         this.assignDocWin();
21971         
21972         
21973         this.activated = true;
21974          
21975     
21976         if(Roo.isGecko){ // prevent silly gecko errors
21977             this.win.focus();
21978             var s = this.win.getSelection();
21979             if(!s.focusNode || s.focusNode.nodeType != 3){
21980                 var r = s.getRangeAt(0);
21981                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21982                 r.collapse(true);
21983                 this.deferFocus();
21984             }
21985             try{
21986                 this.execCmd('useCSS', true);
21987                 this.execCmd('styleWithCSS', false);
21988             }catch(e){}
21989         }
21990         this.owner.fireEvent('activate', this);
21991     },
21992
21993     // private
21994     adjustFont: function(btn){
21995         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21996         //if(Roo.isSafari){ // safari
21997         //    adjust *= 2;
21998        // }
21999         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22000         if(Roo.isSafari){ // safari
22001             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22002             v =  (v < 10) ? 10 : v;
22003             v =  (v > 48) ? 48 : v;
22004             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22005             
22006         }
22007         
22008         
22009         v = Math.max(1, v+adjust);
22010         
22011         this.execCmd('FontSize', v  );
22012     },
22013
22014     onEditorEvent : function(e)
22015     {
22016         this.owner.fireEvent('editorevent', this, e);
22017       //  this.updateToolbar();
22018         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22019     },
22020
22021     insertTag : function(tg)
22022     {
22023         // could be a bit smarter... -> wrap the current selected tRoo..
22024         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22025             
22026             range = this.createRange(this.getSelection());
22027             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22028             wrappingNode.appendChild(range.extractContents());
22029             range.insertNode(wrappingNode);
22030
22031             return;
22032             
22033             
22034             
22035         }
22036         this.execCmd("formatblock",   tg);
22037         
22038     },
22039     
22040     insertText : function(txt)
22041     {
22042         
22043         
22044         var range = this.createRange();
22045         range.deleteContents();
22046                //alert(Sender.getAttribute('label'));
22047                
22048         range.insertNode(this.doc.createTextNode(txt));
22049     } ,
22050     
22051      
22052
22053     /**
22054      * Executes a Midas editor command on the editor document and performs necessary focus and
22055      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22056      * @param {String} cmd The Midas command
22057      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22058      */
22059     relayCmd : function(cmd, value){
22060         this.win.focus();
22061         this.execCmd(cmd, value);
22062         this.owner.fireEvent('editorevent', this);
22063         //this.updateToolbar();
22064         this.owner.deferFocus();
22065     },
22066
22067     /**
22068      * Executes a Midas editor command directly on the editor document.
22069      * For visual commands, you should use {@link #relayCmd} instead.
22070      * <b>This should only be called after the editor is initialized.</b>
22071      * @param {String} cmd The Midas command
22072      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22073      */
22074     execCmd : function(cmd, value){
22075         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22076         this.syncValue();
22077     },
22078  
22079  
22080    
22081     /**
22082      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22083      * to insert tRoo.
22084      * @param {String} text | dom node.. 
22085      */
22086     insertAtCursor : function(text)
22087     {
22088         
22089         if(!this.activated){
22090             return;
22091         }
22092         /*
22093         if(Roo.isIE){
22094             this.win.focus();
22095             var r = this.doc.selection.createRange();
22096             if(r){
22097                 r.collapse(true);
22098                 r.pasteHTML(text);
22099                 this.syncValue();
22100                 this.deferFocus();
22101             
22102             }
22103             return;
22104         }
22105         */
22106         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22107             this.win.focus();
22108             
22109             
22110             // from jquery ui (MIT licenced)
22111             var range, node;
22112             var win = this.win;
22113             
22114             if (win.getSelection && win.getSelection().getRangeAt) {
22115                 range = win.getSelection().getRangeAt(0);
22116                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22117                 range.insertNode(node);
22118             } else if (win.document.selection && win.document.selection.createRange) {
22119                 // no firefox support
22120                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22121                 win.document.selection.createRange().pasteHTML(txt);
22122             } else {
22123                 // no firefox support
22124                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22125                 this.execCmd('InsertHTML', txt);
22126             } 
22127             
22128             this.syncValue();
22129             
22130             this.deferFocus();
22131         }
22132     },
22133  // private
22134     mozKeyPress : function(e){
22135         if(e.ctrlKey){
22136             var c = e.getCharCode(), cmd;
22137           
22138             if(c > 0){
22139                 c = String.fromCharCode(c).toLowerCase();
22140                 switch(c){
22141                     case 'b':
22142                         cmd = 'bold';
22143                         break;
22144                     case 'i':
22145                         cmd = 'italic';
22146                         break;
22147                     
22148                     case 'u':
22149                         cmd = 'underline';
22150                         break;
22151                     
22152                     case 'v':
22153                         this.cleanUpPaste.defer(100, this);
22154                         return;
22155                         
22156                 }
22157                 if(cmd){
22158                     this.win.focus();
22159                     this.execCmd(cmd);
22160                     this.deferFocus();
22161                     e.preventDefault();
22162                 }
22163                 
22164             }
22165         }
22166     },
22167
22168     // private
22169     fixKeys : function(){ // load time branching for fastest keydown performance
22170         if(Roo.isIE){
22171             return function(e){
22172                 var k = e.getKey(), r;
22173                 if(k == e.TAB){
22174                     e.stopEvent();
22175                     r = this.doc.selection.createRange();
22176                     if(r){
22177                         r.collapse(true);
22178                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22179                         this.deferFocus();
22180                     }
22181                     return;
22182                 }
22183                 
22184                 if(k == e.ENTER){
22185                     r = this.doc.selection.createRange();
22186                     if(r){
22187                         var target = r.parentElement();
22188                         if(!target || target.tagName.toLowerCase() != 'li'){
22189                             e.stopEvent();
22190                             r.pasteHTML('<br />');
22191                             r.collapse(false);
22192                             r.select();
22193                         }
22194                     }
22195                 }
22196                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22197                     this.cleanUpPaste.defer(100, this);
22198                     return;
22199                 }
22200                 
22201                 
22202             };
22203         }else if(Roo.isOpera){
22204             return function(e){
22205                 var k = e.getKey();
22206                 if(k == e.TAB){
22207                     e.stopEvent();
22208                     this.win.focus();
22209                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22210                     this.deferFocus();
22211                 }
22212                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22213                     this.cleanUpPaste.defer(100, this);
22214                     return;
22215                 }
22216                 
22217             };
22218         }else if(Roo.isSafari){
22219             return function(e){
22220                 var k = e.getKey();
22221                 
22222                 if(k == e.TAB){
22223                     e.stopEvent();
22224                     this.execCmd('InsertText','\t');
22225                     this.deferFocus();
22226                     return;
22227                 }
22228                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22229                     this.cleanUpPaste.defer(100, this);
22230                     return;
22231                 }
22232                 
22233              };
22234         }
22235     }(),
22236     
22237     getAllAncestors: function()
22238     {
22239         var p = this.getSelectedNode();
22240         var a = [];
22241         if (!p) {
22242             a.push(p); // push blank onto stack..
22243             p = this.getParentElement();
22244         }
22245         
22246         
22247         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22248             a.push(p);
22249             p = p.parentNode;
22250         }
22251         a.push(this.doc.body);
22252         return a;
22253     },
22254     lastSel : false,
22255     lastSelNode : false,
22256     
22257     
22258     getSelection : function() 
22259     {
22260         this.assignDocWin();
22261         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22262     },
22263     
22264     getSelectedNode: function() 
22265     {
22266         // this may only work on Gecko!!!
22267         
22268         // should we cache this!!!!
22269         
22270         
22271         
22272          
22273         var range = this.createRange(this.getSelection()).cloneRange();
22274         
22275         if (Roo.isIE) {
22276             var parent = range.parentElement();
22277             while (true) {
22278                 var testRange = range.duplicate();
22279                 testRange.moveToElementText(parent);
22280                 if (testRange.inRange(range)) {
22281                     break;
22282                 }
22283                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22284                     break;
22285                 }
22286                 parent = parent.parentElement;
22287             }
22288             return parent;
22289         }
22290         
22291         // is ancestor a text element.
22292         var ac =  range.commonAncestorContainer;
22293         if (ac.nodeType == 3) {
22294             ac = ac.parentNode;
22295         }
22296         
22297         var ar = ac.childNodes;
22298          
22299         var nodes = [];
22300         var other_nodes = [];
22301         var has_other_nodes = false;
22302         for (var i=0;i<ar.length;i++) {
22303             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22304                 continue;
22305             }
22306             // fullly contained node.
22307             
22308             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22309                 nodes.push(ar[i]);
22310                 continue;
22311             }
22312             
22313             // probably selected..
22314             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22315                 other_nodes.push(ar[i]);
22316                 continue;
22317             }
22318             // outer..
22319             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22320                 continue;
22321             }
22322             
22323             
22324             has_other_nodes = true;
22325         }
22326         if (!nodes.length && other_nodes.length) {
22327             nodes= other_nodes;
22328         }
22329         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22330             return false;
22331         }
22332         
22333         return nodes[0];
22334     },
22335     createRange: function(sel)
22336     {
22337         // this has strange effects when using with 
22338         // top toolbar - not sure if it's a great idea.
22339         //this.editor.contentWindow.focus();
22340         if (typeof sel != "undefined") {
22341             try {
22342                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22343             } catch(e) {
22344                 return this.doc.createRange();
22345             }
22346         } else {
22347             return this.doc.createRange();
22348         }
22349     },
22350     getParentElement: function()
22351     {
22352         
22353         this.assignDocWin();
22354         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22355         
22356         var range = this.createRange(sel);
22357          
22358         try {
22359             var p = range.commonAncestorContainer;
22360             while (p.nodeType == 3) { // text node
22361                 p = p.parentNode;
22362             }
22363             return p;
22364         } catch (e) {
22365             return null;
22366         }
22367     
22368     },
22369     /***
22370      *
22371      * Range intersection.. the hard stuff...
22372      *  '-1' = before
22373      *  '0' = hits..
22374      *  '1' = after.
22375      *         [ -- selected range --- ]
22376      *   [fail]                        [fail]
22377      *
22378      *    basically..
22379      *      if end is before start or  hits it. fail.
22380      *      if start is after end or hits it fail.
22381      *
22382      *   if either hits (but other is outside. - then it's not 
22383      *   
22384      *    
22385      **/
22386     
22387     
22388     // @see http://www.thismuchiknow.co.uk/?p=64.
22389     rangeIntersectsNode : function(range, node)
22390     {
22391         var nodeRange = node.ownerDocument.createRange();
22392         try {
22393             nodeRange.selectNode(node);
22394         } catch (e) {
22395             nodeRange.selectNodeContents(node);
22396         }
22397     
22398         var rangeStartRange = range.cloneRange();
22399         rangeStartRange.collapse(true);
22400     
22401         var rangeEndRange = range.cloneRange();
22402         rangeEndRange.collapse(false);
22403     
22404         var nodeStartRange = nodeRange.cloneRange();
22405         nodeStartRange.collapse(true);
22406     
22407         var nodeEndRange = nodeRange.cloneRange();
22408         nodeEndRange.collapse(false);
22409     
22410         return rangeStartRange.compareBoundaryPoints(
22411                  Range.START_TO_START, nodeEndRange) == -1 &&
22412                rangeEndRange.compareBoundaryPoints(
22413                  Range.START_TO_START, nodeStartRange) == 1;
22414         
22415          
22416     },
22417     rangeCompareNode : function(range, node)
22418     {
22419         var nodeRange = node.ownerDocument.createRange();
22420         try {
22421             nodeRange.selectNode(node);
22422         } catch (e) {
22423             nodeRange.selectNodeContents(node);
22424         }
22425         
22426         
22427         range.collapse(true);
22428     
22429         nodeRange.collapse(true);
22430      
22431         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22432         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22433          
22434         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22435         
22436         var nodeIsBefore   =  ss == 1;
22437         var nodeIsAfter    = ee == -1;
22438         
22439         if (nodeIsBefore && nodeIsAfter) {
22440             return 0; // outer
22441         }
22442         if (!nodeIsBefore && nodeIsAfter) {
22443             return 1; //right trailed.
22444         }
22445         
22446         if (nodeIsBefore && !nodeIsAfter) {
22447             return 2;  // left trailed.
22448         }
22449         // fully contined.
22450         return 3;
22451     },
22452
22453     // private? - in a new class?
22454     cleanUpPaste :  function()
22455     {
22456         // cleans up the whole document..
22457         Roo.log('cleanuppaste');
22458         
22459         this.cleanUpChildren(this.doc.body);
22460         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22461         if (clean != this.doc.body.innerHTML) {
22462             this.doc.body.innerHTML = clean;
22463         }
22464         
22465     },
22466     
22467     cleanWordChars : function(input) {// change the chars to hex code
22468         var he = Roo.HtmlEditorCore;
22469         
22470         var output = input;
22471         Roo.each(he.swapCodes, function(sw) { 
22472             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22473             
22474             output = output.replace(swapper, sw[1]);
22475         });
22476         
22477         return output;
22478     },
22479     
22480     
22481     cleanUpChildren : function (n)
22482     {
22483         if (!n.childNodes.length) {
22484             return;
22485         }
22486         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22487            this.cleanUpChild(n.childNodes[i]);
22488         }
22489     },
22490     
22491     
22492         
22493     
22494     cleanUpChild : function (node)
22495     {
22496         var ed = this;
22497         //console.log(node);
22498         if (node.nodeName == "#text") {
22499             // clean up silly Windows -- stuff?
22500             return; 
22501         }
22502         if (node.nodeName == "#comment") {
22503             node.parentNode.removeChild(node);
22504             // clean up silly Windows -- stuff?
22505             return; 
22506         }
22507         var lcname = node.tagName.toLowerCase();
22508         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22509         // whitelist of tags..
22510         
22511         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22512             // remove node.
22513             node.parentNode.removeChild(node);
22514             return;
22515             
22516         }
22517         
22518         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22519         
22520         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22521         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22522         
22523         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22524         //    remove_keep_children = true;
22525         //}
22526         
22527         if (remove_keep_children) {
22528             this.cleanUpChildren(node);
22529             // inserts everything just before this node...
22530             while (node.childNodes.length) {
22531                 var cn = node.childNodes[0];
22532                 node.removeChild(cn);
22533                 node.parentNode.insertBefore(cn, node);
22534             }
22535             node.parentNode.removeChild(node);
22536             return;
22537         }
22538         
22539         if (!node.attributes || !node.attributes.length) {
22540             this.cleanUpChildren(node);
22541             return;
22542         }
22543         
22544         function cleanAttr(n,v)
22545         {
22546             
22547             if (v.match(/^\./) || v.match(/^\//)) {
22548                 return;
22549             }
22550             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22551                 return;
22552             }
22553             if (v.match(/^#/)) {
22554                 return;
22555             }
22556 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22557             node.removeAttribute(n);
22558             
22559         }
22560         
22561         var cwhite = this.cwhite;
22562         var cblack = this.cblack;
22563             
22564         function cleanStyle(n,v)
22565         {
22566             if (v.match(/expression/)) { //XSS?? should we even bother..
22567                 node.removeAttribute(n);
22568                 return;
22569             }
22570             
22571             var parts = v.split(/;/);
22572             var clean = [];
22573             
22574             Roo.each(parts, function(p) {
22575                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22576                 if (!p.length) {
22577                     return true;
22578                 }
22579                 var l = p.split(':').shift().replace(/\s+/g,'');
22580                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22581                 
22582                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22583 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22584                     //node.removeAttribute(n);
22585                     return true;
22586                 }
22587                 //Roo.log()
22588                 // only allow 'c whitelisted system attributes'
22589                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22590 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22591                     //node.removeAttribute(n);
22592                     return true;
22593                 }
22594                 
22595                 
22596                  
22597                 
22598                 clean.push(p);
22599                 return true;
22600             });
22601             if (clean.length) { 
22602                 node.setAttribute(n, clean.join(';'));
22603             } else {
22604                 node.removeAttribute(n);
22605             }
22606             
22607         }
22608         
22609         
22610         for (var i = node.attributes.length-1; i > -1 ; i--) {
22611             var a = node.attributes[i];
22612             //console.log(a);
22613             
22614             if (a.name.toLowerCase().substr(0,2)=='on')  {
22615                 node.removeAttribute(a.name);
22616                 continue;
22617             }
22618             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22619                 node.removeAttribute(a.name);
22620                 continue;
22621             }
22622             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22623                 cleanAttr(a.name,a.value); // fixme..
22624                 continue;
22625             }
22626             if (a.name == 'style') {
22627                 cleanStyle(a.name,a.value);
22628                 continue;
22629             }
22630             /// clean up MS crap..
22631             // tecnically this should be a list of valid class'es..
22632             
22633             
22634             if (a.name == 'class') {
22635                 if (a.value.match(/^Mso/)) {
22636                     node.className = '';
22637                 }
22638                 
22639                 if (a.value.match(/^body$/)) {
22640                     node.className = '';
22641                 }
22642                 continue;
22643             }
22644             
22645             // style cleanup!?
22646             // class cleanup?
22647             
22648         }
22649         
22650         
22651         this.cleanUpChildren(node);
22652         
22653         
22654     },
22655     
22656     /**
22657      * Clean up MS wordisms...
22658      */
22659     cleanWord : function(node)
22660     {
22661         
22662         
22663         if (!node) {
22664             this.cleanWord(this.doc.body);
22665             return;
22666         }
22667         if (node.nodeName == "#text") {
22668             // clean up silly Windows -- stuff?
22669             return; 
22670         }
22671         if (node.nodeName == "#comment") {
22672             node.parentNode.removeChild(node);
22673             // clean up silly Windows -- stuff?
22674             return; 
22675         }
22676         
22677         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22678             node.parentNode.removeChild(node);
22679             return;
22680         }
22681         
22682         // remove - but keep children..
22683         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22684             while (node.childNodes.length) {
22685                 var cn = node.childNodes[0];
22686                 node.removeChild(cn);
22687                 node.parentNode.insertBefore(cn, node);
22688             }
22689             node.parentNode.removeChild(node);
22690             this.iterateChildren(node, this.cleanWord);
22691             return;
22692         }
22693         // clean styles
22694         if (node.className.length) {
22695             
22696             var cn = node.className.split(/\W+/);
22697             var cna = [];
22698             Roo.each(cn, function(cls) {
22699                 if (cls.match(/Mso[a-zA-Z]+/)) {
22700                     return;
22701                 }
22702                 cna.push(cls);
22703             });
22704             node.className = cna.length ? cna.join(' ') : '';
22705             if (!cna.length) {
22706                 node.removeAttribute("class");
22707             }
22708         }
22709         
22710         if (node.hasAttribute("lang")) {
22711             node.removeAttribute("lang");
22712         }
22713         
22714         if (node.hasAttribute("style")) {
22715             
22716             var styles = node.getAttribute("style").split(";");
22717             var nstyle = [];
22718             Roo.each(styles, function(s) {
22719                 if (!s.match(/:/)) {
22720                     return;
22721                 }
22722                 var kv = s.split(":");
22723                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22724                     return;
22725                 }
22726                 // what ever is left... we allow.
22727                 nstyle.push(s);
22728             });
22729             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22730             if (!nstyle.length) {
22731                 node.removeAttribute('style');
22732             }
22733         }
22734         this.iterateChildren(node, this.cleanWord);
22735         
22736         
22737         
22738     },
22739     /**
22740      * iterateChildren of a Node, calling fn each time, using this as the scole..
22741      * @param {DomNode} node node to iterate children of.
22742      * @param {Function} fn method of this class to call on each item.
22743      */
22744     iterateChildren : function(node, fn)
22745     {
22746         if (!node.childNodes.length) {
22747                 return;
22748         }
22749         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22750            fn.call(this, node.childNodes[i])
22751         }
22752     },
22753     
22754     
22755     /**
22756      * cleanTableWidths.
22757      *
22758      * Quite often pasting from word etc.. results in tables with column and widths.
22759      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22760      *
22761      */
22762     cleanTableWidths : function(node)
22763     {
22764          
22765          
22766         if (!node) {
22767             this.cleanTableWidths(this.doc.body);
22768             return;
22769         }
22770         
22771         // ignore list...
22772         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22773             return; 
22774         }
22775         Roo.log(node.tagName);
22776         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22777             this.iterateChildren(node, this.cleanTableWidths);
22778             return;
22779         }
22780         if (node.hasAttribute('width')) {
22781             node.removeAttribute('width');
22782         }
22783         
22784          
22785         if (node.hasAttribute("style")) {
22786             // pretty basic...
22787             
22788             var styles = node.getAttribute("style").split(";");
22789             var nstyle = [];
22790             Roo.each(styles, function(s) {
22791                 if (!s.match(/:/)) {
22792                     return;
22793                 }
22794                 var kv = s.split(":");
22795                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22796                     return;
22797                 }
22798                 // what ever is left... we allow.
22799                 nstyle.push(s);
22800             });
22801             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22802             if (!nstyle.length) {
22803                 node.removeAttribute('style');
22804             }
22805         }
22806         
22807         this.iterateChildren(node, this.cleanTableWidths);
22808         
22809         
22810     },
22811     
22812     
22813     
22814     
22815     domToHTML : function(currentElement, depth, nopadtext) {
22816         
22817         depth = depth || 0;
22818         nopadtext = nopadtext || false;
22819     
22820         if (!currentElement) {
22821             return this.domToHTML(this.doc.body);
22822         }
22823         
22824         //Roo.log(currentElement);
22825         var j;
22826         var allText = false;
22827         var nodeName = currentElement.nodeName;
22828         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22829         
22830         if  (nodeName == '#text') {
22831             
22832             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22833         }
22834         
22835         
22836         var ret = '';
22837         if (nodeName != 'BODY') {
22838              
22839             var i = 0;
22840             // Prints the node tagName, such as <A>, <IMG>, etc
22841             if (tagName) {
22842                 var attr = [];
22843                 for(i = 0; i < currentElement.attributes.length;i++) {
22844                     // quoting?
22845                     var aname = currentElement.attributes.item(i).name;
22846                     if (!currentElement.attributes.item(i).value.length) {
22847                         continue;
22848                     }
22849                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22850                 }
22851                 
22852                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22853             } 
22854             else {
22855                 
22856                 // eack
22857             }
22858         } else {
22859             tagName = false;
22860         }
22861         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22862             return ret;
22863         }
22864         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22865             nopadtext = true;
22866         }
22867         
22868         
22869         // Traverse the tree
22870         i = 0;
22871         var currentElementChild = currentElement.childNodes.item(i);
22872         var allText = true;
22873         var innerHTML  = '';
22874         lastnode = '';
22875         while (currentElementChild) {
22876             // Formatting code (indent the tree so it looks nice on the screen)
22877             var nopad = nopadtext;
22878             if (lastnode == 'SPAN') {
22879                 nopad  = true;
22880             }
22881             // text
22882             if  (currentElementChild.nodeName == '#text') {
22883                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22884                 toadd = nopadtext ? toadd : toadd.trim();
22885                 if (!nopad && toadd.length > 80) {
22886                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22887                 }
22888                 innerHTML  += toadd;
22889                 
22890                 i++;
22891                 currentElementChild = currentElement.childNodes.item(i);
22892                 lastNode = '';
22893                 continue;
22894             }
22895             allText = false;
22896             
22897             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22898                 
22899             // Recursively traverse the tree structure of the child node
22900             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22901             lastnode = currentElementChild.nodeName;
22902             i++;
22903             currentElementChild=currentElement.childNodes.item(i);
22904         }
22905         
22906         ret += innerHTML;
22907         
22908         if (!allText) {
22909                 // The remaining code is mostly for formatting the tree
22910             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22911         }
22912         
22913         
22914         if (tagName) {
22915             ret+= "</"+tagName+">";
22916         }
22917         return ret;
22918         
22919     },
22920         
22921     applyBlacklists : function()
22922     {
22923         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22924         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22925         
22926         this.white = [];
22927         this.black = [];
22928         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22929             if (b.indexOf(tag) > -1) {
22930                 return;
22931             }
22932             this.white.push(tag);
22933             
22934         }, this);
22935         
22936         Roo.each(w, function(tag) {
22937             if (b.indexOf(tag) > -1) {
22938                 return;
22939             }
22940             if (this.white.indexOf(tag) > -1) {
22941                 return;
22942             }
22943             this.white.push(tag);
22944             
22945         }, this);
22946         
22947         
22948         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22949             if (w.indexOf(tag) > -1) {
22950                 return;
22951             }
22952             this.black.push(tag);
22953             
22954         }, this);
22955         
22956         Roo.each(b, function(tag) {
22957             if (w.indexOf(tag) > -1) {
22958                 return;
22959             }
22960             if (this.black.indexOf(tag) > -1) {
22961                 return;
22962             }
22963             this.black.push(tag);
22964             
22965         }, this);
22966         
22967         
22968         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22969         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22970         
22971         this.cwhite = [];
22972         this.cblack = [];
22973         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22974             if (b.indexOf(tag) > -1) {
22975                 return;
22976             }
22977             this.cwhite.push(tag);
22978             
22979         }, this);
22980         
22981         Roo.each(w, function(tag) {
22982             if (b.indexOf(tag) > -1) {
22983                 return;
22984             }
22985             if (this.cwhite.indexOf(tag) > -1) {
22986                 return;
22987             }
22988             this.cwhite.push(tag);
22989             
22990         }, this);
22991         
22992         
22993         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22994             if (w.indexOf(tag) > -1) {
22995                 return;
22996             }
22997             this.cblack.push(tag);
22998             
22999         }, this);
23000         
23001         Roo.each(b, function(tag) {
23002             if (w.indexOf(tag) > -1) {
23003                 return;
23004             }
23005             if (this.cblack.indexOf(tag) > -1) {
23006                 return;
23007             }
23008             this.cblack.push(tag);
23009             
23010         }, this);
23011     },
23012     
23013     setStylesheets : function(stylesheets)
23014     {
23015         if(typeof(stylesheets) == 'string'){
23016             Roo.get(this.iframe.contentDocument.head).createChild({
23017                 tag : 'link',
23018                 rel : 'stylesheet',
23019                 type : 'text/css',
23020                 href : stylesheets
23021             });
23022             
23023             return;
23024         }
23025         var _this = this;
23026      
23027         Roo.each(stylesheets, function(s) {
23028             if(!s.length){
23029                 return;
23030             }
23031             
23032             Roo.get(_this.iframe.contentDocument.head).createChild({
23033                 tag : 'link',
23034                 rel : 'stylesheet',
23035                 type : 'text/css',
23036                 href : s
23037             });
23038         });
23039
23040         
23041     },
23042     
23043     removeStylesheets : function()
23044     {
23045         var _this = this;
23046         
23047         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23048             s.remove();
23049         });
23050     },
23051     
23052     setStyle : function(style)
23053     {
23054         Roo.get(this.iframe.contentDocument.head).createChild({
23055             tag : 'style',
23056             type : 'text/css',
23057             html : style
23058         });
23059
23060         return;
23061     }
23062     
23063     // hide stuff that is not compatible
23064     /**
23065      * @event blur
23066      * @hide
23067      */
23068     /**
23069      * @event change
23070      * @hide
23071      */
23072     /**
23073      * @event focus
23074      * @hide
23075      */
23076     /**
23077      * @event specialkey
23078      * @hide
23079      */
23080     /**
23081      * @cfg {String} fieldClass @hide
23082      */
23083     /**
23084      * @cfg {String} focusClass @hide
23085      */
23086     /**
23087      * @cfg {String} autoCreate @hide
23088      */
23089     /**
23090      * @cfg {String} inputType @hide
23091      */
23092     /**
23093      * @cfg {String} invalidClass @hide
23094      */
23095     /**
23096      * @cfg {String} invalidText @hide
23097      */
23098     /**
23099      * @cfg {String} msgFx @hide
23100      */
23101     /**
23102      * @cfg {String} validateOnBlur @hide
23103      */
23104 });
23105
23106 Roo.HtmlEditorCore.white = [
23107         'area', 'br', 'img', 'input', 'hr', 'wbr',
23108         
23109        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23110        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23111        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23112        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23113        'table',   'ul',         'xmp', 
23114        
23115        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23116       'thead',   'tr', 
23117      
23118       'dir', 'menu', 'ol', 'ul', 'dl',
23119        
23120       'embed',  'object'
23121 ];
23122
23123
23124 Roo.HtmlEditorCore.black = [
23125     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23126         'applet', // 
23127         'base',   'basefont', 'bgsound', 'blink',  'body', 
23128         'frame',  'frameset', 'head',    'html',   'ilayer', 
23129         'iframe', 'layer',  'link',     'meta',    'object',   
23130         'script', 'style' ,'title',  'xml' // clean later..
23131 ];
23132 Roo.HtmlEditorCore.clean = [
23133     'script', 'style', 'title', 'xml'
23134 ];
23135 Roo.HtmlEditorCore.remove = [
23136     'font'
23137 ];
23138 // attributes..
23139
23140 Roo.HtmlEditorCore.ablack = [
23141     'on'
23142 ];
23143     
23144 Roo.HtmlEditorCore.aclean = [ 
23145     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23146 ];
23147
23148 // protocols..
23149 Roo.HtmlEditorCore.pwhite= [
23150         'http',  'https',  'mailto'
23151 ];
23152
23153 // white listed style attributes.
23154 Roo.HtmlEditorCore.cwhite= [
23155       //  'text-align', /// default is to allow most things..
23156       
23157          
23158 //        'font-size'//??
23159 ];
23160
23161 // black listed style attributes.
23162 Roo.HtmlEditorCore.cblack= [
23163       //  'font-size' -- this can be set by the project 
23164 ];
23165
23166
23167 Roo.HtmlEditorCore.swapCodes   =[ 
23168     [    8211, "--" ], 
23169     [    8212, "--" ], 
23170     [    8216,  "'" ],  
23171     [    8217, "'" ],  
23172     [    8220, '"' ],  
23173     [    8221, '"' ],  
23174     [    8226, "*" ],  
23175     [    8230, "..." ]
23176 ]; 
23177
23178     /*
23179  * - LGPL
23180  *
23181  * HtmlEditor
23182  * 
23183  */
23184
23185 /**
23186  * @class Roo.bootstrap.HtmlEditor
23187  * @extends Roo.bootstrap.TextArea
23188  * Bootstrap HtmlEditor class
23189
23190  * @constructor
23191  * Create a new HtmlEditor
23192  * @param {Object} config The config object
23193  */
23194
23195 Roo.bootstrap.HtmlEditor = function(config){
23196     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23197     if (!this.toolbars) {
23198         this.toolbars = [];
23199     }
23200     
23201     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23202     this.addEvents({
23203             /**
23204              * @event initialize
23205              * Fires when the editor is fully initialized (including the iframe)
23206              * @param {HtmlEditor} this
23207              */
23208             initialize: true,
23209             /**
23210              * @event activate
23211              * Fires when the editor is first receives the focus. Any insertion must wait
23212              * until after this event.
23213              * @param {HtmlEditor} this
23214              */
23215             activate: true,
23216              /**
23217              * @event beforesync
23218              * Fires before the textarea is updated with content from the editor iframe. Return false
23219              * to cancel the sync.
23220              * @param {HtmlEditor} this
23221              * @param {String} html
23222              */
23223             beforesync: true,
23224              /**
23225              * @event beforepush
23226              * Fires before the iframe editor is updated with content from the textarea. Return false
23227              * to cancel the push.
23228              * @param {HtmlEditor} this
23229              * @param {String} html
23230              */
23231             beforepush: true,
23232              /**
23233              * @event sync
23234              * Fires when the textarea is updated with content from the editor iframe.
23235              * @param {HtmlEditor} this
23236              * @param {String} html
23237              */
23238             sync: true,
23239              /**
23240              * @event push
23241              * Fires when the iframe editor is updated with content from the textarea.
23242              * @param {HtmlEditor} this
23243              * @param {String} html
23244              */
23245             push: true,
23246              /**
23247              * @event editmodechange
23248              * Fires when the editor switches edit modes
23249              * @param {HtmlEditor} this
23250              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23251              */
23252             editmodechange: true,
23253             /**
23254              * @event editorevent
23255              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23256              * @param {HtmlEditor} this
23257              */
23258             editorevent: true,
23259             /**
23260              * @event firstfocus
23261              * Fires when on first focus - needed by toolbars..
23262              * @param {HtmlEditor} this
23263              */
23264             firstfocus: true,
23265             /**
23266              * @event autosave
23267              * Auto save the htmlEditor value as a file into Events
23268              * @param {HtmlEditor} this
23269              */
23270             autosave: true,
23271             /**
23272              * @event savedpreview
23273              * preview the saved version of htmlEditor
23274              * @param {HtmlEditor} this
23275              */
23276             savedpreview: true
23277         });
23278 };
23279
23280
23281 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23282     
23283     
23284       /**
23285      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23286      */
23287     toolbars : false,
23288     
23289      /**
23290     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23291     */
23292     btns : [],
23293    
23294      /**
23295      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23296      *                        Roo.resizable.
23297      */
23298     resizable : false,
23299      /**
23300      * @cfg {Number} height (in pixels)
23301      */   
23302     height: 300,
23303    /**
23304      * @cfg {Number} width (in pixels)
23305      */   
23306     width: false,
23307     
23308     /**
23309      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23310      * 
23311      */
23312     stylesheets: false,
23313     
23314     // id of frame..
23315     frameId: false,
23316     
23317     // private properties
23318     validationEvent : false,
23319     deferHeight: true,
23320     initialized : false,
23321     activated : false,
23322     
23323     onFocus : Roo.emptyFn,
23324     iframePad:3,
23325     hideMode:'offsets',
23326     
23327     tbContainer : false,
23328     
23329     bodyCls : '',
23330     
23331     toolbarContainer :function() {
23332         return this.wrap.select('.x-html-editor-tb',true).first();
23333     },
23334
23335     /**
23336      * Protected method that will not generally be called directly. It
23337      * is called when the editor creates its toolbar. Override this method if you need to
23338      * add custom toolbar buttons.
23339      * @param {HtmlEditor} editor
23340      */
23341     createToolbar : function(){
23342         Roo.log('renewing');
23343         Roo.log("create toolbars");
23344         
23345         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23346         this.toolbars[0].render(this.toolbarContainer());
23347         
23348         return;
23349         
23350 //        if (!editor.toolbars || !editor.toolbars.length) {
23351 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23352 //        }
23353 //        
23354 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23355 //            editor.toolbars[i] = Roo.factory(
23356 //                    typeof(editor.toolbars[i]) == 'string' ?
23357 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23358 //                Roo.bootstrap.HtmlEditor);
23359 //            editor.toolbars[i].init(editor);
23360 //        }
23361     },
23362
23363      
23364     // private
23365     onRender : function(ct, position)
23366     {
23367        // Roo.log("Call onRender: " + this.xtype);
23368         var _t = this;
23369         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23370       
23371         this.wrap = this.inputEl().wrap({
23372             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23373         });
23374         
23375         this.editorcore.onRender(ct, position);
23376          
23377         if (this.resizable) {
23378             this.resizeEl = new Roo.Resizable(this.wrap, {
23379                 pinned : true,
23380                 wrap: true,
23381                 dynamic : true,
23382                 minHeight : this.height,
23383                 height: this.height,
23384                 handles : this.resizable,
23385                 width: this.width,
23386                 listeners : {
23387                     resize : function(r, w, h) {
23388                         _t.onResize(w,h); // -something
23389                     }
23390                 }
23391             });
23392             
23393         }
23394         this.createToolbar(this);
23395        
23396         
23397         if(!this.width && this.resizable){
23398             this.setSize(this.wrap.getSize());
23399         }
23400         if (this.resizeEl) {
23401             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23402             // should trigger onReize..
23403         }
23404         
23405     },
23406
23407     // private
23408     onResize : function(w, h)
23409     {
23410         Roo.log('resize: ' +w + ',' + h );
23411         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23412         var ew = false;
23413         var eh = false;
23414         
23415         if(this.inputEl() ){
23416             if(typeof w == 'number'){
23417                 var aw = w - this.wrap.getFrameWidth('lr');
23418                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23419                 ew = aw;
23420             }
23421             if(typeof h == 'number'){
23422                  var tbh = -11;  // fixme it needs to tool bar size!
23423                 for (var i =0; i < this.toolbars.length;i++) {
23424                     // fixme - ask toolbars for heights?
23425                     tbh += this.toolbars[i].el.getHeight();
23426                     //if (this.toolbars[i].footer) {
23427                     //    tbh += this.toolbars[i].footer.el.getHeight();
23428                     //}
23429                 }
23430               
23431                 
23432                 
23433                 
23434                 
23435                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23436                 ah -= 5; // knock a few pixes off for look..
23437                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23438                 var eh = ah;
23439             }
23440         }
23441         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23442         this.editorcore.onResize(ew,eh);
23443         
23444     },
23445
23446     /**
23447      * Toggles the editor between standard and source edit mode.
23448      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23449      */
23450     toggleSourceEdit : function(sourceEditMode)
23451     {
23452         this.editorcore.toggleSourceEdit(sourceEditMode);
23453         
23454         if(this.editorcore.sourceEditMode){
23455             Roo.log('editor - showing textarea');
23456             
23457 //            Roo.log('in');
23458 //            Roo.log(this.syncValue());
23459             this.syncValue();
23460             this.inputEl().removeClass(['hide', 'x-hidden']);
23461             this.inputEl().dom.removeAttribute('tabIndex');
23462             this.inputEl().focus();
23463         }else{
23464             Roo.log('editor - hiding textarea');
23465 //            Roo.log('out')
23466 //            Roo.log(this.pushValue()); 
23467             this.pushValue();
23468             
23469             this.inputEl().addClass(['hide', 'x-hidden']);
23470             this.inputEl().dom.setAttribute('tabIndex', -1);
23471             //this.deferFocus();
23472         }
23473          
23474         if(this.resizable){
23475             this.setSize(this.wrap.getSize());
23476         }
23477         
23478         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23479     },
23480  
23481     // private (for BoxComponent)
23482     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23483
23484     // private (for BoxComponent)
23485     getResizeEl : function(){
23486         return this.wrap;
23487     },
23488
23489     // private (for BoxComponent)
23490     getPositionEl : function(){
23491         return this.wrap;
23492     },
23493
23494     // private
23495     initEvents : function(){
23496         this.originalValue = this.getValue();
23497     },
23498
23499 //    /**
23500 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23501 //     * @method
23502 //     */
23503 //    markInvalid : Roo.emptyFn,
23504 //    /**
23505 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23506 //     * @method
23507 //     */
23508 //    clearInvalid : Roo.emptyFn,
23509
23510     setValue : function(v){
23511         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23512         this.editorcore.pushValue();
23513     },
23514
23515      
23516     // private
23517     deferFocus : function(){
23518         this.focus.defer(10, this);
23519     },
23520
23521     // doc'ed in Field
23522     focus : function(){
23523         this.editorcore.focus();
23524         
23525     },
23526       
23527
23528     // private
23529     onDestroy : function(){
23530         
23531         
23532         
23533         if(this.rendered){
23534             
23535             for (var i =0; i < this.toolbars.length;i++) {
23536                 // fixme - ask toolbars for heights?
23537                 this.toolbars[i].onDestroy();
23538             }
23539             
23540             this.wrap.dom.innerHTML = '';
23541             this.wrap.remove();
23542         }
23543     },
23544
23545     // private
23546     onFirstFocus : function(){
23547         //Roo.log("onFirstFocus");
23548         this.editorcore.onFirstFocus();
23549          for (var i =0; i < this.toolbars.length;i++) {
23550             this.toolbars[i].onFirstFocus();
23551         }
23552         
23553     },
23554     
23555     // private
23556     syncValue : function()
23557     {   
23558         this.editorcore.syncValue();
23559     },
23560     
23561     pushValue : function()
23562     {   
23563         this.editorcore.pushValue();
23564     }
23565      
23566     
23567     // hide stuff that is not compatible
23568     /**
23569      * @event blur
23570      * @hide
23571      */
23572     /**
23573      * @event change
23574      * @hide
23575      */
23576     /**
23577      * @event focus
23578      * @hide
23579      */
23580     /**
23581      * @event specialkey
23582      * @hide
23583      */
23584     /**
23585      * @cfg {String} fieldClass @hide
23586      */
23587     /**
23588      * @cfg {String} focusClass @hide
23589      */
23590     /**
23591      * @cfg {String} autoCreate @hide
23592      */
23593     /**
23594      * @cfg {String} inputType @hide
23595      */
23596     /**
23597      * @cfg {String} invalidClass @hide
23598      */
23599     /**
23600      * @cfg {String} invalidText @hide
23601      */
23602     /**
23603      * @cfg {String} msgFx @hide
23604      */
23605     /**
23606      * @cfg {String} validateOnBlur @hide
23607      */
23608 });
23609  
23610     
23611    
23612    
23613    
23614       
23615 Roo.namespace('Roo.bootstrap.htmleditor');
23616 /**
23617  * @class Roo.bootstrap.HtmlEditorToolbar1
23618  * Basic Toolbar
23619  * 
23620  * Usage:
23621  *
23622  new Roo.bootstrap.HtmlEditor({
23623     ....
23624     toolbars : [
23625         new Roo.bootstrap.HtmlEditorToolbar1({
23626             disable : { fonts: 1 , format: 1, ..., ... , ...],
23627             btns : [ .... ]
23628         })
23629     }
23630      
23631  * 
23632  * @cfg {Object} disable List of elements to disable..
23633  * @cfg {Array} btns List of additional buttons.
23634  * 
23635  * 
23636  * NEEDS Extra CSS? 
23637  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23638  */
23639  
23640 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23641 {
23642     
23643     Roo.apply(this, config);
23644     
23645     // default disabled, based on 'good practice'..
23646     this.disable = this.disable || {};
23647     Roo.applyIf(this.disable, {
23648         fontSize : true,
23649         colors : true,
23650         specialElements : true
23651     });
23652     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23653     
23654     this.editor = config.editor;
23655     this.editorcore = config.editor.editorcore;
23656     
23657     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23658     
23659     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23660     // dont call parent... till later.
23661 }
23662 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23663      
23664     bar : true,
23665     
23666     editor : false,
23667     editorcore : false,
23668     
23669     
23670     formats : [
23671         "p" ,  
23672         "h1","h2","h3","h4","h5","h6", 
23673         "pre", "code", 
23674         "abbr", "acronym", "address", "cite", "samp", "var",
23675         'div','span'
23676     ],
23677     
23678     onRender : function(ct, position)
23679     {
23680        // Roo.log("Call onRender: " + this.xtype);
23681         
23682        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23683        Roo.log(this.el);
23684        this.el.dom.style.marginBottom = '0';
23685        var _this = this;
23686        var editorcore = this.editorcore;
23687        var editor= this.editor;
23688        
23689        var children = [];
23690        var btn = function(id,cmd , toggle, handler, html){
23691        
23692             var  event = toggle ? 'toggle' : 'click';
23693        
23694             var a = {
23695                 size : 'sm',
23696                 xtype: 'Button',
23697                 xns: Roo.bootstrap,
23698                 glyphicon : id,
23699                 cmd : id || cmd,
23700                 enableToggle:toggle !== false,
23701                 html : html || '',
23702                 pressed : toggle ? false : null,
23703                 listeners : {}
23704             };
23705             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23706                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23707             };
23708             children.push(a);
23709             return a;
23710        }
23711        
23712     //    var cb_box = function...
23713         
23714         var style = {
23715                 xtype: 'Button',
23716                 size : 'sm',
23717                 xns: Roo.bootstrap,
23718                 glyphicon : 'font',
23719                 //html : 'submit'
23720                 menu : {
23721                     xtype: 'Menu',
23722                     xns: Roo.bootstrap,
23723                     items:  []
23724                 }
23725         };
23726         Roo.each(this.formats, function(f) {
23727             style.menu.items.push({
23728                 xtype :'MenuItem',
23729                 xns: Roo.bootstrap,
23730                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23731                 tagname : f,
23732                 listeners : {
23733                     click : function()
23734                     {
23735                         editorcore.insertTag(this.tagname);
23736                         editor.focus();
23737                     }
23738                 }
23739                 
23740             });
23741         });
23742         children.push(style);   
23743         
23744         btn('bold',false,true);
23745         btn('italic',false,true);
23746         btn('align-left', 'justifyleft',true);
23747         btn('align-center', 'justifycenter',true);
23748         btn('align-right' , 'justifyright',true);
23749         btn('link', false, false, function(btn) {
23750             //Roo.log("create link?");
23751             var url = prompt(this.createLinkText, this.defaultLinkValue);
23752             if(url && url != 'http:/'+'/'){
23753                 this.editorcore.relayCmd('createlink', url);
23754             }
23755         }),
23756         btn('list','insertunorderedlist',true);
23757         btn('pencil', false,true, function(btn){
23758                 Roo.log(this);
23759                 this.toggleSourceEdit(btn.pressed);
23760         });
23761         
23762         if (this.editor.btns.length > 0) {
23763             for (var i = 0; i<this.editor.btns.length; i++) {
23764                 children.push(this.editor.btns[i]);
23765             }
23766         }
23767         
23768         /*
23769         var cog = {
23770                 xtype: 'Button',
23771                 size : 'sm',
23772                 xns: Roo.bootstrap,
23773                 glyphicon : 'cog',
23774                 //html : 'submit'
23775                 menu : {
23776                     xtype: 'Menu',
23777                     xns: Roo.bootstrap,
23778                     items:  []
23779                 }
23780         };
23781         
23782         cog.menu.items.push({
23783             xtype :'MenuItem',
23784             xns: Roo.bootstrap,
23785             html : Clean styles,
23786             tagname : f,
23787             listeners : {
23788                 click : function()
23789                 {
23790                     editorcore.insertTag(this.tagname);
23791                     editor.focus();
23792                 }
23793             }
23794             
23795         });
23796        */
23797         
23798          
23799        this.xtype = 'NavSimplebar';
23800         
23801         for(var i=0;i< children.length;i++) {
23802             
23803             this.buttons.add(this.addxtypeChild(children[i]));
23804             
23805         }
23806         
23807         editor.on('editorevent', this.updateToolbar, this);
23808     },
23809     onBtnClick : function(id)
23810     {
23811        this.editorcore.relayCmd(id);
23812        this.editorcore.focus();
23813     },
23814     
23815     /**
23816      * Protected method that will not generally be called directly. It triggers
23817      * a toolbar update by reading the markup state of the current selection in the editor.
23818      */
23819     updateToolbar: function(){
23820
23821         if(!this.editorcore.activated){
23822             this.editor.onFirstFocus(); // is this neeed?
23823             return;
23824         }
23825
23826         var btns = this.buttons; 
23827         var doc = this.editorcore.doc;
23828         btns.get('bold').setActive(doc.queryCommandState('bold'));
23829         btns.get('italic').setActive(doc.queryCommandState('italic'));
23830         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23831         
23832         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23833         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23834         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23835         
23836         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23837         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23838          /*
23839         
23840         var ans = this.editorcore.getAllAncestors();
23841         if (this.formatCombo) {
23842             
23843             
23844             var store = this.formatCombo.store;
23845             this.formatCombo.setValue("");
23846             for (var i =0; i < ans.length;i++) {
23847                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23848                     // select it..
23849                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23850                     break;
23851                 }
23852             }
23853         }
23854         
23855         
23856         
23857         // hides menus... - so this cant be on a menu...
23858         Roo.bootstrap.MenuMgr.hideAll();
23859         */
23860         Roo.bootstrap.MenuMgr.hideAll();
23861         //this.editorsyncValue();
23862     },
23863     onFirstFocus: function() {
23864         this.buttons.each(function(item){
23865            item.enable();
23866         });
23867     },
23868     toggleSourceEdit : function(sourceEditMode){
23869         
23870           
23871         if(sourceEditMode){
23872             Roo.log("disabling buttons");
23873            this.buttons.each( function(item){
23874                 if(item.cmd != 'pencil'){
23875                     item.disable();
23876                 }
23877             });
23878           
23879         }else{
23880             Roo.log("enabling buttons");
23881             if(this.editorcore.initialized){
23882                 this.buttons.each( function(item){
23883                     item.enable();
23884                 });
23885             }
23886             
23887         }
23888         Roo.log("calling toggole on editor");
23889         // tell the editor that it's been pressed..
23890         this.editor.toggleSourceEdit(sourceEditMode);
23891        
23892     }
23893 });
23894
23895
23896
23897
23898
23899 /**
23900  * @class Roo.bootstrap.Table.AbstractSelectionModel
23901  * @extends Roo.util.Observable
23902  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23903  * implemented by descendant classes.  This class should not be directly instantiated.
23904  * @constructor
23905  */
23906 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23907     this.locked = false;
23908     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23909 };
23910
23911
23912 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23913     /** @ignore Called by the grid automatically. Do not call directly. */
23914     init : function(grid){
23915         this.grid = grid;
23916         this.initEvents();
23917     },
23918
23919     /**
23920      * Locks the selections.
23921      */
23922     lock : function(){
23923         this.locked = true;
23924     },
23925
23926     /**
23927      * Unlocks the selections.
23928      */
23929     unlock : function(){
23930         this.locked = false;
23931     },
23932
23933     /**
23934      * Returns true if the selections are locked.
23935      * @return {Boolean}
23936      */
23937     isLocked : function(){
23938         return this.locked;
23939     }
23940 });
23941 /**
23942  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23943  * @class Roo.bootstrap.Table.RowSelectionModel
23944  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23945  * It supports multiple selections and keyboard selection/navigation. 
23946  * @constructor
23947  * @param {Object} config
23948  */
23949
23950 Roo.bootstrap.Table.RowSelectionModel = function(config){
23951     Roo.apply(this, config);
23952     this.selections = new Roo.util.MixedCollection(false, function(o){
23953         return o.id;
23954     });
23955
23956     this.last = false;
23957     this.lastActive = false;
23958
23959     this.addEvents({
23960         /**
23961              * @event selectionchange
23962              * Fires when the selection changes
23963              * @param {SelectionModel} this
23964              */
23965             "selectionchange" : true,
23966         /**
23967              * @event afterselectionchange
23968              * Fires after the selection changes (eg. by key press or clicking)
23969              * @param {SelectionModel} this
23970              */
23971             "afterselectionchange" : true,
23972         /**
23973              * @event beforerowselect
23974              * Fires when a row is selected being selected, return false to cancel.
23975              * @param {SelectionModel} this
23976              * @param {Number} rowIndex The selected index
23977              * @param {Boolean} keepExisting False if other selections will be cleared
23978              */
23979             "beforerowselect" : true,
23980         /**
23981              * @event rowselect
23982              * Fires when a row is selected.
23983              * @param {SelectionModel} this
23984              * @param {Number} rowIndex The selected index
23985              * @param {Roo.data.Record} r The record
23986              */
23987             "rowselect" : true,
23988         /**
23989              * @event rowdeselect
23990              * Fires when a row is deselected.
23991              * @param {SelectionModel} this
23992              * @param {Number} rowIndex The selected index
23993              */
23994         "rowdeselect" : true
23995     });
23996     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23997     this.locked = false;
23998  };
23999
24000 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24001     /**
24002      * @cfg {Boolean} singleSelect
24003      * True to allow selection of only one row at a time (defaults to false)
24004      */
24005     singleSelect : false,
24006
24007     // private
24008     initEvents : function()
24009     {
24010
24011         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24012         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24013         //}else{ // allow click to work like normal
24014          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24015         //}
24016         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24017         this.grid.on("rowclick", this.handleMouseDown, this);
24018         
24019         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24020             "up" : function(e){
24021                 if(!e.shiftKey){
24022                     this.selectPrevious(e.shiftKey);
24023                 }else if(this.last !== false && this.lastActive !== false){
24024                     var last = this.last;
24025                     this.selectRange(this.last,  this.lastActive-1);
24026                     this.grid.getView().focusRow(this.lastActive);
24027                     if(last !== false){
24028                         this.last = last;
24029                     }
24030                 }else{
24031                     this.selectFirstRow();
24032                 }
24033                 this.fireEvent("afterselectionchange", this);
24034             },
24035             "down" : function(e){
24036                 if(!e.shiftKey){
24037                     this.selectNext(e.shiftKey);
24038                 }else if(this.last !== false && this.lastActive !== false){
24039                     var last = this.last;
24040                     this.selectRange(this.last,  this.lastActive+1);
24041                     this.grid.getView().focusRow(this.lastActive);
24042                     if(last !== false){
24043                         this.last = last;
24044                     }
24045                 }else{
24046                     this.selectFirstRow();
24047                 }
24048                 this.fireEvent("afterselectionchange", this);
24049             },
24050             scope: this
24051         });
24052         this.grid.store.on('load', function(){
24053             this.selections.clear();
24054         },this);
24055         /*
24056         var view = this.grid.view;
24057         view.on("refresh", this.onRefresh, this);
24058         view.on("rowupdated", this.onRowUpdated, this);
24059         view.on("rowremoved", this.onRemove, this);
24060         */
24061     },
24062
24063     // private
24064     onRefresh : function()
24065     {
24066         var ds = this.grid.store, i, v = this.grid.view;
24067         var s = this.selections;
24068         s.each(function(r){
24069             if((i = ds.indexOfId(r.id)) != -1){
24070                 v.onRowSelect(i);
24071             }else{
24072                 s.remove(r);
24073             }
24074         });
24075     },
24076
24077     // private
24078     onRemove : function(v, index, r){
24079         this.selections.remove(r);
24080     },
24081
24082     // private
24083     onRowUpdated : function(v, index, r){
24084         if(this.isSelected(r)){
24085             v.onRowSelect(index);
24086         }
24087     },
24088
24089     /**
24090      * Select records.
24091      * @param {Array} records The records to select
24092      * @param {Boolean} keepExisting (optional) True to keep existing selections
24093      */
24094     selectRecords : function(records, keepExisting)
24095     {
24096         if(!keepExisting){
24097             this.clearSelections();
24098         }
24099             var ds = this.grid.store;
24100         for(var i = 0, len = records.length; i < len; i++){
24101             this.selectRow(ds.indexOf(records[i]), true);
24102         }
24103     },
24104
24105     /**
24106      * Gets the number of selected rows.
24107      * @return {Number}
24108      */
24109     getCount : function(){
24110         return this.selections.length;
24111     },
24112
24113     /**
24114      * Selects the first row in the grid.
24115      */
24116     selectFirstRow : function(){
24117         this.selectRow(0);
24118     },
24119
24120     /**
24121      * Select the last row.
24122      * @param {Boolean} keepExisting (optional) True to keep existing selections
24123      */
24124     selectLastRow : function(keepExisting){
24125         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24126         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24127     },
24128
24129     /**
24130      * Selects the row immediately following the last selected row.
24131      * @param {Boolean} keepExisting (optional) True to keep existing selections
24132      */
24133     selectNext : function(keepExisting)
24134     {
24135             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24136             this.selectRow(this.last+1, keepExisting);
24137             this.grid.getView().focusRow(this.last);
24138         }
24139     },
24140
24141     /**
24142      * Selects the row that precedes the last selected row.
24143      * @param {Boolean} keepExisting (optional) True to keep existing selections
24144      */
24145     selectPrevious : function(keepExisting){
24146         if(this.last){
24147             this.selectRow(this.last-1, keepExisting);
24148             this.grid.getView().focusRow(this.last);
24149         }
24150     },
24151
24152     /**
24153      * Returns the selected records
24154      * @return {Array} Array of selected records
24155      */
24156     getSelections : function(){
24157         return [].concat(this.selections.items);
24158     },
24159
24160     /**
24161      * Returns the first selected record.
24162      * @return {Record}
24163      */
24164     getSelected : function(){
24165         return this.selections.itemAt(0);
24166     },
24167
24168
24169     /**
24170      * Clears all selections.
24171      */
24172     clearSelections : function(fast)
24173     {
24174         if(this.locked) {
24175             return;
24176         }
24177         if(fast !== true){
24178                 var ds = this.grid.store;
24179             var s = this.selections;
24180             s.each(function(r){
24181                 this.deselectRow(ds.indexOfId(r.id));
24182             }, this);
24183             s.clear();
24184         }else{
24185             this.selections.clear();
24186         }
24187         this.last = false;
24188     },
24189
24190
24191     /**
24192      * Selects all rows.
24193      */
24194     selectAll : function(){
24195         if(this.locked) {
24196             return;
24197         }
24198         this.selections.clear();
24199         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24200             this.selectRow(i, true);
24201         }
24202     },
24203
24204     /**
24205      * Returns True if there is a selection.
24206      * @return {Boolean}
24207      */
24208     hasSelection : function(){
24209         return this.selections.length > 0;
24210     },
24211
24212     /**
24213      * Returns True if the specified row is selected.
24214      * @param {Number/Record} record The record or index of the record to check
24215      * @return {Boolean}
24216      */
24217     isSelected : function(index){
24218             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24219         return (r && this.selections.key(r.id) ? true : false);
24220     },
24221
24222     /**
24223      * Returns True if the specified record id is selected.
24224      * @param {String} id The id of record to check
24225      * @return {Boolean}
24226      */
24227     isIdSelected : function(id){
24228         return (this.selections.key(id) ? true : false);
24229     },
24230
24231
24232     // private
24233     handleMouseDBClick : function(e, t){
24234         
24235     },
24236     // private
24237     handleMouseDown : function(e, t)
24238     {
24239             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24240         if(this.isLocked() || rowIndex < 0 ){
24241             return;
24242         };
24243         if(e.shiftKey && this.last !== false){
24244             var last = this.last;
24245             this.selectRange(last, rowIndex, e.ctrlKey);
24246             this.last = last; // reset the last
24247             t.focus();
24248     
24249         }else{
24250             var isSelected = this.isSelected(rowIndex);
24251             //Roo.log("select row:" + rowIndex);
24252             if(isSelected){
24253                 this.deselectRow(rowIndex);
24254             } else {
24255                         this.selectRow(rowIndex, true);
24256             }
24257     
24258             /*
24259                 if(e.button !== 0 && isSelected){
24260                 alert('rowIndex 2: ' + rowIndex);
24261                     view.focusRow(rowIndex);
24262                 }else if(e.ctrlKey && isSelected){
24263                     this.deselectRow(rowIndex);
24264                 }else if(!isSelected){
24265                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24266                     view.focusRow(rowIndex);
24267                 }
24268             */
24269         }
24270         this.fireEvent("afterselectionchange", this);
24271     },
24272     // private
24273     handleDragableRowClick :  function(grid, rowIndex, e) 
24274     {
24275         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24276             this.selectRow(rowIndex, false);
24277             grid.view.focusRow(rowIndex);
24278              this.fireEvent("afterselectionchange", this);
24279         }
24280     },
24281     
24282     /**
24283      * Selects multiple rows.
24284      * @param {Array} rows Array of the indexes of the row to select
24285      * @param {Boolean} keepExisting (optional) True to keep existing selections
24286      */
24287     selectRows : function(rows, keepExisting){
24288         if(!keepExisting){
24289             this.clearSelections();
24290         }
24291         for(var i = 0, len = rows.length; i < len; i++){
24292             this.selectRow(rows[i], true);
24293         }
24294     },
24295
24296     /**
24297      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24298      * @param {Number} startRow The index of the first row in the range
24299      * @param {Number} endRow The index of the last row in the range
24300      * @param {Boolean} keepExisting (optional) True to retain existing selections
24301      */
24302     selectRange : function(startRow, endRow, keepExisting){
24303         if(this.locked) {
24304             return;
24305         }
24306         if(!keepExisting){
24307             this.clearSelections();
24308         }
24309         if(startRow <= endRow){
24310             for(var i = startRow; i <= endRow; i++){
24311                 this.selectRow(i, true);
24312             }
24313         }else{
24314             for(var i = startRow; i >= endRow; i--){
24315                 this.selectRow(i, true);
24316             }
24317         }
24318     },
24319
24320     /**
24321      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24322      * @param {Number} startRow The index of the first row in the range
24323      * @param {Number} endRow The index of the last row in the range
24324      */
24325     deselectRange : function(startRow, endRow, preventViewNotify){
24326         if(this.locked) {
24327             return;
24328         }
24329         for(var i = startRow; i <= endRow; i++){
24330             this.deselectRow(i, preventViewNotify);
24331         }
24332     },
24333
24334     /**
24335      * Selects a row.
24336      * @param {Number} row The index of the row to select
24337      * @param {Boolean} keepExisting (optional) True to keep existing selections
24338      */
24339     selectRow : function(index, keepExisting, preventViewNotify)
24340     {
24341             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24342             return;
24343         }
24344         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24345             if(!keepExisting || this.singleSelect){
24346                 this.clearSelections();
24347             }
24348             
24349             var r = this.grid.store.getAt(index);
24350             //console.log('selectRow - record id :' + r.id);
24351             
24352             this.selections.add(r);
24353             this.last = this.lastActive = index;
24354             if(!preventViewNotify){
24355                 var proxy = new Roo.Element(
24356                                 this.grid.getRowDom(index)
24357                 );
24358                 proxy.addClass('bg-info info');
24359             }
24360             this.fireEvent("rowselect", this, index, r);
24361             this.fireEvent("selectionchange", this);
24362         }
24363     },
24364
24365     /**
24366      * Deselects a row.
24367      * @param {Number} row The index of the row to deselect
24368      */
24369     deselectRow : function(index, preventViewNotify)
24370     {
24371         if(this.locked) {
24372             return;
24373         }
24374         if(this.last == index){
24375             this.last = false;
24376         }
24377         if(this.lastActive == index){
24378             this.lastActive = false;
24379         }
24380         
24381         var r = this.grid.store.getAt(index);
24382         if (!r) {
24383             return;
24384         }
24385         
24386         this.selections.remove(r);
24387         //.console.log('deselectRow - record id :' + r.id);
24388         if(!preventViewNotify){
24389         
24390             var proxy = new Roo.Element(
24391                 this.grid.getRowDom(index)
24392             );
24393             proxy.removeClass('bg-info info');
24394         }
24395         this.fireEvent("rowdeselect", this, index);
24396         this.fireEvent("selectionchange", this);
24397     },
24398
24399     // private
24400     restoreLast : function(){
24401         if(this._last){
24402             this.last = this._last;
24403         }
24404     },
24405
24406     // private
24407     acceptsNav : function(row, col, cm){
24408         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24409     },
24410
24411     // private
24412     onEditorKey : function(field, e){
24413         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24414         if(k == e.TAB){
24415             e.stopEvent();
24416             ed.completeEdit();
24417             if(e.shiftKey){
24418                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24419             }else{
24420                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24421             }
24422         }else if(k == e.ENTER && !e.ctrlKey){
24423             e.stopEvent();
24424             ed.completeEdit();
24425             if(e.shiftKey){
24426                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24427             }else{
24428                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24429             }
24430         }else if(k == e.ESC){
24431             ed.cancelEdit();
24432         }
24433         if(newCell){
24434             g.startEditing(newCell[0], newCell[1]);
24435         }
24436     }
24437 });
24438 /*
24439  * Based on:
24440  * Ext JS Library 1.1.1
24441  * Copyright(c) 2006-2007, Ext JS, LLC.
24442  *
24443  * Originally Released Under LGPL - original licence link has changed is not relivant.
24444  *
24445  * Fork - LGPL
24446  * <script type="text/javascript">
24447  */
24448  
24449 /**
24450  * @class Roo.bootstrap.PagingToolbar
24451  * @extends Roo.bootstrap.NavSimplebar
24452  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24453  * @constructor
24454  * Create a new PagingToolbar
24455  * @param {Object} config The config object
24456  * @param {Roo.data.Store} store
24457  */
24458 Roo.bootstrap.PagingToolbar = function(config)
24459 {
24460     // old args format still supported... - xtype is prefered..
24461         // created from xtype...
24462     
24463     this.ds = config.dataSource;
24464     
24465     if (config.store && !this.ds) {
24466         this.store= Roo.factory(config.store, Roo.data);
24467         this.ds = this.store;
24468         this.ds.xmodule = this.xmodule || false;
24469     }
24470     
24471     this.toolbarItems = [];
24472     if (config.items) {
24473         this.toolbarItems = config.items;
24474     }
24475     
24476     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24477     
24478     this.cursor = 0;
24479     
24480     if (this.ds) { 
24481         this.bind(this.ds);
24482     }
24483     
24484     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24485     
24486 };
24487
24488 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24489     /**
24490      * @cfg {Roo.data.Store} dataSource
24491      * The underlying data store providing the paged data
24492      */
24493     /**
24494      * @cfg {String/HTMLElement/Element} container
24495      * container The id or element that will contain the toolbar
24496      */
24497     /**
24498      * @cfg {Boolean} displayInfo
24499      * True to display the displayMsg (defaults to false)
24500      */
24501     /**
24502      * @cfg {Number} pageSize
24503      * The number of records to display per page (defaults to 20)
24504      */
24505     pageSize: 20,
24506     /**
24507      * @cfg {String} displayMsg
24508      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24509      */
24510     displayMsg : 'Displaying {0} - {1} of {2}',
24511     /**
24512      * @cfg {String} emptyMsg
24513      * The message to display when no records are found (defaults to "No data to display")
24514      */
24515     emptyMsg : 'No data to display',
24516     /**
24517      * Customizable piece of the default paging text (defaults to "Page")
24518      * @type String
24519      */
24520     beforePageText : "Page",
24521     /**
24522      * Customizable piece of the default paging text (defaults to "of %0")
24523      * @type String
24524      */
24525     afterPageText : "of {0}",
24526     /**
24527      * Customizable piece of the default paging text (defaults to "First Page")
24528      * @type String
24529      */
24530     firstText : "First Page",
24531     /**
24532      * Customizable piece of the default paging text (defaults to "Previous Page")
24533      * @type String
24534      */
24535     prevText : "Previous Page",
24536     /**
24537      * Customizable piece of the default paging text (defaults to "Next Page")
24538      * @type String
24539      */
24540     nextText : "Next Page",
24541     /**
24542      * Customizable piece of the default paging text (defaults to "Last Page")
24543      * @type String
24544      */
24545     lastText : "Last Page",
24546     /**
24547      * Customizable piece of the default paging text (defaults to "Refresh")
24548      * @type String
24549      */
24550     refreshText : "Refresh",
24551
24552     buttons : false,
24553     // private
24554     onRender : function(ct, position) 
24555     {
24556         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24557         this.navgroup.parentId = this.id;
24558         this.navgroup.onRender(this.el, null);
24559         // add the buttons to the navgroup
24560         
24561         if(this.displayInfo){
24562             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24563             this.displayEl = this.el.select('.x-paging-info', true).first();
24564 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24565 //            this.displayEl = navel.el.select('span',true).first();
24566         }
24567         
24568         var _this = this;
24569         
24570         if(this.buttons){
24571             Roo.each(_this.buttons, function(e){ // this might need to use render????
24572                Roo.factory(e).render(_this.el);
24573             });
24574         }
24575             
24576         Roo.each(_this.toolbarItems, function(e) {
24577             _this.navgroup.addItem(e);
24578         });
24579         
24580         
24581         this.first = this.navgroup.addItem({
24582             tooltip: this.firstText,
24583             cls: "prev",
24584             icon : 'fa fa-backward',
24585             disabled: true,
24586             preventDefault: true,
24587             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24588         });
24589         
24590         this.prev =  this.navgroup.addItem({
24591             tooltip: this.prevText,
24592             cls: "prev",
24593             icon : 'fa fa-step-backward',
24594             disabled: true,
24595             preventDefault: true,
24596             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24597         });
24598     //this.addSeparator();
24599         
24600         
24601         var field = this.navgroup.addItem( {
24602             tagtype : 'span',
24603             cls : 'x-paging-position',
24604             
24605             html : this.beforePageText  +
24606                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24607                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24608          } ); //?? escaped?
24609         
24610         this.field = field.el.select('input', true).first();
24611         this.field.on("keydown", this.onPagingKeydown, this);
24612         this.field.on("focus", function(){this.dom.select();});
24613     
24614     
24615         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24616         //this.field.setHeight(18);
24617         //this.addSeparator();
24618         this.next = this.navgroup.addItem({
24619             tooltip: this.nextText,
24620             cls: "next",
24621             html : ' <i class="fa fa-step-forward">',
24622             disabled: true,
24623             preventDefault: true,
24624             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24625         });
24626         this.last = this.navgroup.addItem({
24627             tooltip: this.lastText,
24628             icon : 'fa fa-forward',
24629             cls: "next",
24630             disabled: true,
24631             preventDefault: true,
24632             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24633         });
24634     //this.addSeparator();
24635         this.loading = this.navgroup.addItem({
24636             tooltip: this.refreshText,
24637             icon: 'fa fa-refresh',
24638             preventDefault: true,
24639             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24640         });
24641         
24642     },
24643
24644     // private
24645     updateInfo : function(){
24646         if(this.displayEl){
24647             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24648             var msg = count == 0 ?
24649                 this.emptyMsg :
24650                 String.format(
24651                     this.displayMsg,
24652                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24653                 );
24654             this.displayEl.update(msg);
24655         }
24656     },
24657
24658     // private
24659     onLoad : function(ds, r, o)
24660     {
24661         this.cursor = o.params.start ? o.params.start : 0;
24662         
24663         var d = this.getPageData(),
24664             ap = d.activePage,
24665             ps = d.pages;
24666         
24667         
24668         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24669         this.field.dom.value = ap;
24670         this.first.setDisabled(ap == 1);
24671         this.prev.setDisabled(ap == 1);
24672         this.next.setDisabled(ap == ps);
24673         this.last.setDisabled(ap == ps);
24674         this.loading.enable();
24675         this.updateInfo();
24676     },
24677
24678     // private
24679     getPageData : function(){
24680         var total = this.ds.getTotalCount();
24681         return {
24682             total : total,
24683             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24684             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24685         };
24686     },
24687
24688     // private
24689     onLoadError : function(){
24690         this.loading.enable();
24691     },
24692
24693     // private
24694     onPagingKeydown : function(e){
24695         var k = e.getKey();
24696         var d = this.getPageData();
24697         if(k == e.RETURN){
24698             var v = this.field.dom.value, pageNum;
24699             if(!v || isNaN(pageNum = parseInt(v, 10))){
24700                 this.field.dom.value = d.activePage;
24701                 return;
24702             }
24703             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24704             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24705             e.stopEvent();
24706         }
24707         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))
24708         {
24709           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24710           this.field.dom.value = pageNum;
24711           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24712           e.stopEvent();
24713         }
24714         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24715         {
24716           var v = this.field.dom.value, pageNum; 
24717           var increment = (e.shiftKey) ? 10 : 1;
24718           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24719                 increment *= -1;
24720           }
24721           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24722             this.field.dom.value = d.activePage;
24723             return;
24724           }
24725           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24726           {
24727             this.field.dom.value = parseInt(v, 10) + increment;
24728             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24729             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24730           }
24731           e.stopEvent();
24732         }
24733     },
24734
24735     // private
24736     beforeLoad : function(){
24737         if(this.loading){
24738             this.loading.disable();
24739         }
24740     },
24741
24742     // private
24743     onClick : function(which){
24744         
24745         var ds = this.ds;
24746         if (!ds) {
24747             return;
24748         }
24749         
24750         switch(which){
24751             case "first":
24752                 ds.load({params:{start: 0, limit: this.pageSize}});
24753             break;
24754             case "prev":
24755                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24756             break;
24757             case "next":
24758                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24759             break;
24760             case "last":
24761                 var total = ds.getTotalCount();
24762                 var extra = total % this.pageSize;
24763                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24764                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24765             break;
24766             case "refresh":
24767                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24768             break;
24769         }
24770     },
24771
24772     /**
24773      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24774      * @param {Roo.data.Store} store The data store to unbind
24775      */
24776     unbind : function(ds){
24777         ds.un("beforeload", this.beforeLoad, this);
24778         ds.un("load", this.onLoad, this);
24779         ds.un("loadexception", this.onLoadError, this);
24780         ds.un("remove", this.updateInfo, this);
24781         ds.un("add", this.updateInfo, this);
24782         this.ds = undefined;
24783     },
24784
24785     /**
24786      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24787      * @param {Roo.data.Store} store The data store to bind
24788      */
24789     bind : function(ds){
24790         ds.on("beforeload", this.beforeLoad, this);
24791         ds.on("load", this.onLoad, this);
24792         ds.on("loadexception", this.onLoadError, this);
24793         ds.on("remove", this.updateInfo, this);
24794         ds.on("add", this.updateInfo, this);
24795         this.ds = ds;
24796     }
24797 });/*
24798  * - LGPL
24799  *
24800  * element
24801  * 
24802  */
24803
24804 /**
24805  * @class Roo.bootstrap.MessageBar
24806  * @extends Roo.bootstrap.Component
24807  * Bootstrap MessageBar class
24808  * @cfg {String} html contents of the MessageBar
24809  * @cfg {String} weight (info | success | warning | danger) default info
24810  * @cfg {String} beforeClass insert the bar before the given class
24811  * @cfg {Boolean} closable (true | false) default false
24812  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24813  * 
24814  * @constructor
24815  * Create a new Element
24816  * @param {Object} config The config object
24817  */
24818
24819 Roo.bootstrap.MessageBar = function(config){
24820     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24821 };
24822
24823 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24824     
24825     html: '',
24826     weight: 'info',
24827     closable: false,
24828     fixed: false,
24829     beforeClass: 'bootstrap-sticky-wrap',
24830     
24831     getAutoCreate : function(){
24832         
24833         var cfg = {
24834             tag: 'div',
24835             cls: 'alert alert-dismissable alert-' + this.weight,
24836             cn: [
24837                 {
24838                     tag: 'span',
24839                     cls: 'message',
24840                     html: this.html || ''
24841                 }
24842             ]
24843         };
24844         
24845         if(this.fixed){
24846             cfg.cls += ' alert-messages-fixed';
24847         }
24848         
24849         if(this.closable){
24850             cfg.cn.push({
24851                 tag: 'button',
24852                 cls: 'close',
24853                 html: 'x'
24854             });
24855         }
24856         
24857         return cfg;
24858     },
24859     
24860     onRender : function(ct, position)
24861     {
24862         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24863         
24864         if(!this.el){
24865             var cfg = Roo.apply({},  this.getAutoCreate());
24866             cfg.id = Roo.id();
24867             
24868             if (this.cls) {
24869                 cfg.cls += ' ' + this.cls;
24870             }
24871             if (this.style) {
24872                 cfg.style = this.style;
24873             }
24874             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24875             
24876             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24877         }
24878         
24879         this.el.select('>button.close').on('click', this.hide, this);
24880         
24881     },
24882     
24883     show : function()
24884     {
24885         if (!this.rendered) {
24886             this.render();
24887         }
24888         
24889         this.el.show();
24890         
24891         this.fireEvent('show', this);
24892         
24893     },
24894     
24895     hide : function()
24896     {
24897         if (!this.rendered) {
24898             this.render();
24899         }
24900         
24901         this.el.hide();
24902         
24903         this.fireEvent('hide', this);
24904     },
24905     
24906     update : function()
24907     {
24908 //        var e = this.el.dom.firstChild;
24909 //        
24910 //        if(this.closable){
24911 //            e = e.nextSibling;
24912 //        }
24913 //        
24914 //        e.data = this.html || '';
24915
24916         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24917     }
24918    
24919 });
24920
24921  
24922
24923      /*
24924  * - LGPL
24925  *
24926  * Graph
24927  * 
24928  */
24929
24930
24931 /**
24932  * @class Roo.bootstrap.Graph
24933  * @extends Roo.bootstrap.Component
24934  * Bootstrap Graph class
24935 > Prameters
24936  -sm {number} sm 4
24937  -md {number} md 5
24938  @cfg {String} graphtype  bar | vbar | pie
24939  @cfg {number} g_x coodinator | centre x (pie)
24940  @cfg {number} g_y coodinator | centre y (pie)
24941  @cfg {number} g_r radius (pie)
24942  @cfg {number} g_height height of the chart (respected by all elements in the set)
24943  @cfg {number} g_width width of the chart (respected by all elements in the set)
24944  @cfg {Object} title The title of the chart
24945     
24946  -{Array}  values
24947  -opts (object) options for the chart 
24948      o {
24949      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24950      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24951      o vgutter (number)
24952      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.
24953      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24954      o to
24955      o stretch (boolean)
24956      o }
24957  -opts (object) options for the pie
24958      o{
24959      o cut
24960      o startAngle (number)
24961      o endAngle (number)
24962      } 
24963  *
24964  * @constructor
24965  * Create a new Input
24966  * @param {Object} config The config object
24967  */
24968
24969 Roo.bootstrap.Graph = function(config){
24970     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24971     
24972     this.addEvents({
24973         // img events
24974         /**
24975          * @event click
24976          * The img click event for the img.
24977          * @param {Roo.EventObject} e
24978          */
24979         "click" : true
24980     });
24981 };
24982
24983 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24984     
24985     sm: 4,
24986     md: 5,
24987     graphtype: 'bar',
24988     g_height: 250,
24989     g_width: 400,
24990     g_x: 50,
24991     g_y: 50,
24992     g_r: 30,
24993     opts:{
24994         //g_colors: this.colors,
24995         g_type: 'soft',
24996         g_gutter: '20%'
24997
24998     },
24999     title : false,
25000
25001     getAutoCreate : function(){
25002         
25003         var cfg = {
25004             tag: 'div',
25005             html : null
25006         };
25007         
25008         
25009         return  cfg;
25010     },
25011
25012     onRender : function(ct,position){
25013         
25014         
25015         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25016         
25017         if (typeof(Raphael) == 'undefined') {
25018             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25019             return;
25020         }
25021         
25022         this.raphael = Raphael(this.el.dom);
25023         
25024                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25025                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25026                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25027                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25028                 /*
25029                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25030                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25031                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25032                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25033                 
25034                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25035                 r.barchart(330, 10, 300, 220, data1);
25036                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25037                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25038                 */
25039                 
25040                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25041                 // r.barchart(30, 30, 560, 250,  xdata, {
25042                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25043                 //     axis : "0 0 1 1",
25044                 //     axisxlabels :  xdata
25045                 //     //yvalues : cols,
25046                    
25047                 // });
25048 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25049 //        
25050 //        this.load(null,xdata,{
25051 //                axis : "0 0 1 1",
25052 //                axisxlabels :  xdata
25053 //                });
25054
25055     },
25056
25057     load : function(graphtype,xdata,opts)
25058     {
25059         this.raphael.clear();
25060         if(!graphtype) {
25061             graphtype = this.graphtype;
25062         }
25063         if(!opts){
25064             opts = this.opts;
25065         }
25066         var r = this.raphael,
25067             fin = function () {
25068                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25069             },
25070             fout = function () {
25071                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25072             },
25073             pfin = function() {
25074                 this.sector.stop();
25075                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25076
25077                 if (this.label) {
25078                     this.label[0].stop();
25079                     this.label[0].attr({ r: 7.5 });
25080                     this.label[1].attr({ "font-weight": 800 });
25081                 }
25082             },
25083             pfout = function() {
25084                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25085
25086                 if (this.label) {
25087                     this.label[0].animate({ r: 5 }, 500, "bounce");
25088                     this.label[1].attr({ "font-weight": 400 });
25089                 }
25090             };
25091
25092         switch(graphtype){
25093             case 'bar':
25094                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25095                 break;
25096             case 'hbar':
25097                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25098                 break;
25099             case 'pie':
25100 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25101 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25102 //            
25103                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25104                 
25105                 break;
25106
25107         }
25108         
25109         if(this.title){
25110             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25111         }
25112         
25113     },
25114     
25115     setTitle: function(o)
25116     {
25117         this.title = o;
25118     },
25119     
25120     initEvents: function() {
25121         
25122         if(!this.href){
25123             this.el.on('click', this.onClick, this);
25124         }
25125     },
25126     
25127     onClick : function(e)
25128     {
25129         Roo.log('img onclick');
25130         this.fireEvent('click', this, e);
25131     }
25132    
25133 });
25134
25135  
25136 /*
25137  * - LGPL
25138  *
25139  * numberBox
25140  * 
25141  */
25142 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25143
25144 /**
25145  * @class Roo.bootstrap.dash.NumberBox
25146  * @extends Roo.bootstrap.Component
25147  * Bootstrap NumberBox class
25148  * @cfg {String} headline Box headline
25149  * @cfg {String} content Box content
25150  * @cfg {String} icon Box icon
25151  * @cfg {String} footer Footer text
25152  * @cfg {String} fhref Footer href
25153  * 
25154  * @constructor
25155  * Create a new NumberBox
25156  * @param {Object} config The config object
25157  */
25158
25159
25160 Roo.bootstrap.dash.NumberBox = function(config){
25161     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25162     
25163 };
25164
25165 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25166     
25167     headline : '',
25168     content : '',
25169     icon : '',
25170     footer : '',
25171     fhref : '',
25172     ficon : '',
25173     
25174     getAutoCreate : function(){
25175         
25176         var cfg = {
25177             tag : 'div',
25178             cls : 'small-box ',
25179             cn : [
25180                 {
25181                     tag : 'div',
25182                     cls : 'inner',
25183                     cn :[
25184                         {
25185                             tag : 'h3',
25186                             cls : 'roo-headline',
25187                             html : this.headline
25188                         },
25189                         {
25190                             tag : 'p',
25191                             cls : 'roo-content',
25192                             html : this.content
25193                         }
25194                     ]
25195                 }
25196             ]
25197         };
25198         
25199         if(this.icon){
25200             cfg.cn.push({
25201                 tag : 'div',
25202                 cls : 'icon',
25203                 cn :[
25204                     {
25205                         tag : 'i',
25206                         cls : 'ion ' + this.icon
25207                     }
25208                 ]
25209             });
25210         }
25211         
25212         if(this.footer){
25213             var footer = {
25214                 tag : 'a',
25215                 cls : 'small-box-footer',
25216                 href : this.fhref || '#',
25217                 html : this.footer
25218             };
25219             
25220             cfg.cn.push(footer);
25221             
25222         }
25223         
25224         return  cfg;
25225     },
25226
25227     onRender : function(ct,position){
25228         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25229
25230
25231        
25232                 
25233     },
25234
25235     setHeadline: function (value)
25236     {
25237         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25238     },
25239     
25240     setFooter: function (value, href)
25241     {
25242         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25243         
25244         if(href){
25245             this.el.select('a.small-box-footer',true).first().attr('href', href);
25246         }
25247         
25248     },
25249
25250     setContent: function (value)
25251     {
25252         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25253     },
25254
25255     initEvents: function() 
25256     {   
25257         
25258     }
25259     
25260 });
25261
25262  
25263 /*
25264  * - LGPL
25265  *
25266  * TabBox
25267  * 
25268  */
25269 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25270
25271 /**
25272  * @class Roo.bootstrap.dash.TabBox
25273  * @extends Roo.bootstrap.Component
25274  * Bootstrap TabBox class
25275  * @cfg {String} title Title of the TabBox
25276  * @cfg {String} icon Icon of the TabBox
25277  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25278  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25279  * 
25280  * @constructor
25281  * Create a new TabBox
25282  * @param {Object} config The config object
25283  */
25284
25285
25286 Roo.bootstrap.dash.TabBox = function(config){
25287     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25288     this.addEvents({
25289         // raw events
25290         /**
25291          * @event addpane
25292          * When a pane is added
25293          * @param {Roo.bootstrap.dash.TabPane} pane
25294          */
25295         "addpane" : true,
25296         /**
25297          * @event activatepane
25298          * When a pane is activated
25299          * @param {Roo.bootstrap.dash.TabPane} pane
25300          */
25301         "activatepane" : true
25302         
25303          
25304     });
25305     
25306     this.panes = [];
25307 };
25308
25309 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25310
25311     title : '',
25312     icon : false,
25313     showtabs : true,
25314     tabScrollable : false,
25315     
25316     getChildContainer : function()
25317     {
25318         return this.el.select('.tab-content', true).first();
25319     },
25320     
25321     getAutoCreate : function(){
25322         
25323         var header = {
25324             tag: 'li',
25325             cls: 'pull-left header',
25326             html: this.title,
25327             cn : []
25328         };
25329         
25330         if(this.icon){
25331             header.cn.push({
25332                 tag: 'i',
25333                 cls: 'fa ' + this.icon
25334             });
25335         }
25336         
25337         var h = {
25338             tag: 'ul',
25339             cls: 'nav nav-tabs pull-right',
25340             cn: [
25341                 header
25342             ]
25343         };
25344         
25345         if(this.tabScrollable){
25346             h = {
25347                 tag: 'div',
25348                 cls: 'tab-header',
25349                 cn: [
25350                     {
25351                         tag: 'ul',
25352                         cls: 'nav nav-tabs pull-right',
25353                         cn: [
25354                             header
25355                         ]
25356                     }
25357                 ]
25358             };
25359         }
25360         
25361         var cfg = {
25362             tag: 'div',
25363             cls: 'nav-tabs-custom',
25364             cn: [
25365                 h,
25366                 {
25367                     tag: 'div',
25368                     cls: 'tab-content no-padding',
25369                     cn: []
25370                 }
25371             ]
25372         };
25373
25374         return  cfg;
25375     },
25376     initEvents : function()
25377     {
25378         //Roo.log('add add pane handler');
25379         this.on('addpane', this.onAddPane, this);
25380     },
25381      /**
25382      * Updates the box title
25383      * @param {String} html to set the title to.
25384      */
25385     setTitle : function(value)
25386     {
25387         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25388     },
25389     onAddPane : function(pane)
25390     {
25391         this.panes.push(pane);
25392         //Roo.log('addpane');
25393         //Roo.log(pane);
25394         // tabs are rendere left to right..
25395         if(!this.showtabs){
25396             return;
25397         }
25398         
25399         var ctr = this.el.select('.nav-tabs', true).first();
25400          
25401          
25402         var existing = ctr.select('.nav-tab',true);
25403         var qty = existing.getCount();;
25404         
25405         
25406         var tab = ctr.createChild({
25407             tag : 'li',
25408             cls : 'nav-tab' + (qty ? '' : ' active'),
25409             cn : [
25410                 {
25411                     tag : 'a',
25412                     href:'#',
25413                     html : pane.title
25414                 }
25415             ]
25416         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25417         pane.tab = tab;
25418         
25419         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25420         if (!qty) {
25421             pane.el.addClass('active');
25422         }
25423         
25424                 
25425     },
25426     onTabClick : function(ev,un,ob,pane)
25427     {
25428         //Roo.log('tab - prev default');
25429         ev.preventDefault();
25430         
25431         
25432         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25433         pane.tab.addClass('active');
25434         //Roo.log(pane.title);
25435         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25436         // technically we should have a deactivate event.. but maybe add later.
25437         // and it should not de-activate the selected tab...
25438         this.fireEvent('activatepane', pane);
25439         pane.el.addClass('active');
25440         pane.fireEvent('activate');
25441         
25442         
25443     },
25444     
25445     getActivePane : function()
25446     {
25447         var r = false;
25448         Roo.each(this.panes, function(p) {
25449             if(p.el.hasClass('active')){
25450                 r = p;
25451                 return false;
25452             }
25453             
25454             return;
25455         });
25456         
25457         return r;
25458     }
25459     
25460     
25461 });
25462
25463  
25464 /*
25465  * - LGPL
25466  *
25467  * Tab pane
25468  * 
25469  */
25470 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25471 /**
25472  * @class Roo.bootstrap.TabPane
25473  * @extends Roo.bootstrap.Component
25474  * Bootstrap TabPane class
25475  * @cfg {Boolean} active (false | true) Default false
25476  * @cfg {String} title title of panel
25477
25478  * 
25479  * @constructor
25480  * Create a new TabPane
25481  * @param {Object} config The config object
25482  */
25483
25484 Roo.bootstrap.dash.TabPane = function(config){
25485     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25486     
25487     this.addEvents({
25488         // raw events
25489         /**
25490          * @event activate
25491          * When a pane is activated
25492          * @param {Roo.bootstrap.dash.TabPane} pane
25493          */
25494         "activate" : true
25495          
25496     });
25497 };
25498
25499 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25500     
25501     active : false,
25502     title : '',
25503     
25504     // the tabBox that this is attached to.
25505     tab : false,
25506      
25507     getAutoCreate : function() 
25508     {
25509         var cfg = {
25510             tag: 'div',
25511             cls: 'tab-pane'
25512         };
25513         
25514         if(this.active){
25515             cfg.cls += ' active';
25516         }
25517         
25518         return cfg;
25519     },
25520     initEvents  : function()
25521     {
25522         //Roo.log('trigger add pane handler');
25523         this.parent().fireEvent('addpane', this)
25524     },
25525     
25526      /**
25527      * Updates the tab title 
25528      * @param {String} html to set the title to.
25529      */
25530     setTitle: function(str)
25531     {
25532         if (!this.tab) {
25533             return;
25534         }
25535         this.title = str;
25536         this.tab.select('a', true).first().dom.innerHTML = str;
25537         
25538     }
25539     
25540     
25541     
25542 });
25543
25544  
25545
25546
25547  /*
25548  * - LGPL
25549  *
25550  * menu
25551  * 
25552  */
25553 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25554
25555 /**
25556  * @class Roo.bootstrap.menu.Menu
25557  * @extends Roo.bootstrap.Component
25558  * Bootstrap Menu class - container for Menu
25559  * @cfg {String} html Text of the menu
25560  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25561  * @cfg {String} icon Font awesome icon
25562  * @cfg {String} pos Menu align to (top | bottom) default bottom
25563  * 
25564  * 
25565  * @constructor
25566  * Create a new Menu
25567  * @param {Object} config The config object
25568  */
25569
25570
25571 Roo.bootstrap.menu.Menu = function(config){
25572     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25573     
25574     this.addEvents({
25575         /**
25576          * @event beforeshow
25577          * Fires before this menu is displayed
25578          * @param {Roo.bootstrap.menu.Menu} this
25579          */
25580         beforeshow : true,
25581         /**
25582          * @event beforehide
25583          * Fires before this menu is hidden
25584          * @param {Roo.bootstrap.menu.Menu} this
25585          */
25586         beforehide : true,
25587         /**
25588          * @event show
25589          * Fires after this menu is displayed
25590          * @param {Roo.bootstrap.menu.Menu} this
25591          */
25592         show : true,
25593         /**
25594          * @event hide
25595          * Fires after this menu is hidden
25596          * @param {Roo.bootstrap.menu.Menu} this
25597          */
25598         hide : true,
25599         /**
25600          * @event click
25601          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25602          * @param {Roo.bootstrap.menu.Menu} this
25603          * @param {Roo.EventObject} e
25604          */
25605         click : true
25606     });
25607     
25608 };
25609
25610 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25611     
25612     submenu : false,
25613     html : '',
25614     weight : 'default',
25615     icon : false,
25616     pos : 'bottom',
25617     
25618     
25619     getChildContainer : function() {
25620         if(this.isSubMenu){
25621             return this.el;
25622         }
25623         
25624         return this.el.select('ul.dropdown-menu', true).first();  
25625     },
25626     
25627     getAutoCreate : function()
25628     {
25629         var text = [
25630             {
25631                 tag : 'span',
25632                 cls : 'roo-menu-text',
25633                 html : this.html
25634             }
25635         ];
25636         
25637         if(this.icon){
25638             text.unshift({
25639                 tag : 'i',
25640                 cls : 'fa ' + this.icon
25641             })
25642         }
25643         
25644         
25645         var cfg = {
25646             tag : 'div',
25647             cls : 'btn-group',
25648             cn : [
25649                 {
25650                     tag : 'button',
25651                     cls : 'dropdown-button btn btn-' + this.weight,
25652                     cn : text
25653                 },
25654                 {
25655                     tag : 'button',
25656                     cls : 'dropdown-toggle btn btn-' + this.weight,
25657                     cn : [
25658                         {
25659                             tag : 'span',
25660                             cls : 'caret'
25661                         }
25662                     ]
25663                 },
25664                 {
25665                     tag : 'ul',
25666                     cls : 'dropdown-menu'
25667                 }
25668             ]
25669             
25670         };
25671         
25672         if(this.pos == 'top'){
25673             cfg.cls += ' dropup';
25674         }
25675         
25676         if(this.isSubMenu){
25677             cfg = {
25678                 tag : 'ul',
25679                 cls : 'dropdown-menu'
25680             }
25681         }
25682         
25683         return cfg;
25684     },
25685     
25686     onRender : function(ct, position)
25687     {
25688         this.isSubMenu = ct.hasClass('dropdown-submenu');
25689         
25690         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25691     },
25692     
25693     initEvents : function() 
25694     {
25695         if(this.isSubMenu){
25696             return;
25697         }
25698         
25699         this.hidden = true;
25700         
25701         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25702         this.triggerEl.on('click', this.onTriggerPress, this);
25703         
25704         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25705         this.buttonEl.on('click', this.onClick, this);
25706         
25707     },
25708     
25709     list : function()
25710     {
25711         if(this.isSubMenu){
25712             return this.el;
25713         }
25714         
25715         return this.el.select('ul.dropdown-menu', true).first();
25716     },
25717     
25718     onClick : function(e)
25719     {
25720         this.fireEvent("click", this, e);
25721     },
25722     
25723     onTriggerPress  : function(e)
25724     {   
25725         if (this.isVisible()) {
25726             this.hide();
25727         } else {
25728             this.show();
25729         }
25730     },
25731     
25732     isVisible : function(){
25733         return !this.hidden;
25734     },
25735     
25736     show : function()
25737     {
25738         this.fireEvent("beforeshow", this);
25739         
25740         this.hidden = false;
25741         this.el.addClass('open');
25742         
25743         Roo.get(document).on("mouseup", this.onMouseUp, this);
25744         
25745         this.fireEvent("show", this);
25746         
25747         
25748     },
25749     
25750     hide : function()
25751     {
25752         this.fireEvent("beforehide", this);
25753         
25754         this.hidden = true;
25755         this.el.removeClass('open');
25756         
25757         Roo.get(document).un("mouseup", this.onMouseUp);
25758         
25759         this.fireEvent("hide", this);
25760     },
25761     
25762     onMouseUp : function()
25763     {
25764         this.hide();
25765     }
25766     
25767 });
25768
25769  
25770  /*
25771  * - LGPL
25772  *
25773  * menu item
25774  * 
25775  */
25776 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25777
25778 /**
25779  * @class Roo.bootstrap.menu.Item
25780  * @extends Roo.bootstrap.Component
25781  * Bootstrap MenuItem class
25782  * @cfg {Boolean} submenu (true | false) default false
25783  * @cfg {String} html text of the item
25784  * @cfg {String} href the link
25785  * @cfg {Boolean} disable (true | false) default false
25786  * @cfg {Boolean} preventDefault (true | false) default true
25787  * @cfg {String} icon Font awesome icon
25788  * @cfg {String} pos Submenu align to (left | right) default right 
25789  * 
25790  * 
25791  * @constructor
25792  * Create a new Item
25793  * @param {Object} config The config object
25794  */
25795
25796
25797 Roo.bootstrap.menu.Item = function(config){
25798     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25799     this.addEvents({
25800         /**
25801          * @event mouseover
25802          * Fires when the mouse is hovering over this menu
25803          * @param {Roo.bootstrap.menu.Item} this
25804          * @param {Roo.EventObject} e
25805          */
25806         mouseover : true,
25807         /**
25808          * @event mouseout
25809          * Fires when the mouse exits this menu
25810          * @param {Roo.bootstrap.menu.Item} this
25811          * @param {Roo.EventObject} e
25812          */
25813         mouseout : true,
25814         // raw events
25815         /**
25816          * @event click
25817          * The raw click event for the entire grid.
25818          * @param {Roo.EventObject} e
25819          */
25820         click : true
25821     });
25822 };
25823
25824 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25825     
25826     submenu : false,
25827     href : '',
25828     html : '',
25829     preventDefault: true,
25830     disable : false,
25831     icon : false,
25832     pos : 'right',
25833     
25834     getAutoCreate : function()
25835     {
25836         var text = [
25837             {
25838                 tag : 'span',
25839                 cls : 'roo-menu-item-text',
25840                 html : this.html
25841             }
25842         ];
25843         
25844         if(this.icon){
25845             text.unshift({
25846                 tag : 'i',
25847                 cls : 'fa ' + this.icon
25848             })
25849         }
25850         
25851         var cfg = {
25852             tag : 'li',
25853             cn : [
25854                 {
25855                     tag : 'a',
25856                     href : this.href || '#',
25857                     cn : text
25858                 }
25859             ]
25860         };
25861         
25862         if(this.disable){
25863             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25864         }
25865         
25866         if(this.submenu){
25867             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25868             
25869             if(this.pos == 'left'){
25870                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25871             }
25872         }
25873         
25874         return cfg;
25875     },
25876     
25877     initEvents : function() 
25878     {
25879         this.el.on('mouseover', this.onMouseOver, this);
25880         this.el.on('mouseout', this.onMouseOut, this);
25881         
25882         this.el.select('a', true).first().on('click', this.onClick, this);
25883         
25884     },
25885     
25886     onClick : function(e)
25887     {
25888         if(this.preventDefault){
25889             e.preventDefault();
25890         }
25891         
25892         this.fireEvent("click", this, e);
25893     },
25894     
25895     onMouseOver : function(e)
25896     {
25897         if(this.submenu && this.pos == 'left'){
25898             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25899         }
25900         
25901         this.fireEvent("mouseover", this, e);
25902     },
25903     
25904     onMouseOut : function(e)
25905     {
25906         this.fireEvent("mouseout", this, e);
25907     }
25908 });
25909
25910  
25911
25912  /*
25913  * - LGPL
25914  *
25915  * menu separator
25916  * 
25917  */
25918 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25919
25920 /**
25921  * @class Roo.bootstrap.menu.Separator
25922  * @extends Roo.bootstrap.Component
25923  * Bootstrap Separator class
25924  * 
25925  * @constructor
25926  * Create a new Separator
25927  * @param {Object} config The config object
25928  */
25929
25930
25931 Roo.bootstrap.menu.Separator = function(config){
25932     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25933 };
25934
25935 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25936     
25937     getAutoCreate : function(){
25938         var cfg = {
25939             tag : 'li',
25940             cls: 'divider'
25941         };
25942         
25943         return cfg;
25944     }
25945    
25946 });
25947
25948  
25949
25950  /*
25951  * - LGPL
25952  *
25953  * Tooltip
25954  * 
25955  */
25956
25957 /**
25958  * @class Roo.bootstrap.Tooltip
25959  * Bootstrap Tooltip class
25960  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25961  * to determine which dom element triggers the tooltip.
25962  * 
25963  * It needs to add support for additional attributes like tooltip-position
25964  * 
25965  * @constructor
25966  * Create a new Toolti
25967  * @param {Object} config The config object
25968  */
25969
25970 Roo.bootstrap.Tooltip = function(config){
25971     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25972     
25973     this.alignment = Roo.bootstrap.Tooltip.alignment;
25974     
25975     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25976         this.alignment = config.alignment;
25977     }
25978     
25979 };
25980
25981 Roo.apply(Roo.bootstrap.Tooltip, {
25982     /**
25983      * @function init initialize tooltip monitoring.
25984      * @static
25985      */
25986     currentEl : false,
25987     currentTip : false,
25988     currentRegion : false,
25989     
25990     //  init : delay?
25991     
25992     init : function()
25993     {
25994         Roo.get(document).on('mouseover', this.enter ,this);
25995         Roo.get(document).on('mouseout', this.leave, this);
25996          
25997         
25998         this.currentTip = new Roo.bootstrap.Tooltip();
25999     },
26000     
26001     enter : function(ev)
26002     {
26003         var dom = ev.getTarget();
26004         
26005         //Roo.log(['enter',dom]);
26006         var el = Roo.fly(dom);
26007         if (this.currentEl) {
26008             //Roo.log(dom);
26009             //Roo.log(this.currentEl);
26010             //Roo.log(this.currentEl.contains(dom));
26011             if (this.currentEl == el) {
26012                 return;
26013             }
26014             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26015                 return;
26016             }
26017
26018         }
26019         
26020         if (this.currentTip.el) {
26021             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26022         }    
26023         //Roo.log(ev);
26024         
26025         if(!el || el.dom == document){
26026             return;
26027         }
26028         
26029         var bindEl = el;
26030         
26031         // you can not look for children, as if el is the body.. then everythign is the child..
26032         if (!el.attr('tooltip')) { //
26033             if (!el.select("[tooltip]").elements.length) {
26034                 return;
26035             }
26036             // is the mouse over this child...?
26037             bindEl = el.select("[tooltip]").first();
26038             var xy = ev.getXY();
26039             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26040                 //Roo.log("not in region.");
26041                 return;
26042             }
26043             //Roo.log("child element over..");
26044             
26045         }
26046         this.currentEl = bindEl;
26047         this.currentTip.bind(bindEl);
26048         this.currentRegion = Roo.lib.Region.getRegion(dom);
26049         this.currentTip.enter();
26050         
26051     },
26052     leave : function(ev)
26053     {
26054         var dom = ev.getTarget();
26055         //Roo.log(['leave',dom]);
26056         if (!this.currentEl) {
26057             return;
26058         }
26059         
26060         
26061         if (dom != this.currentEl.dom) {
26062             return;
26063         }
26064         var xy = ev.getXY();
26065         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26066             return;
26067         }
26068         // only activate leave if mouse cursor is outside... bounding box..
26069         
26070         
26071         
26072         
26073         if (this.currentTip) {
26074             this.currentTip.leave();
26075         }
26076         //Roo.log('clear currentEl');
26077         this.currentEl = false;
26078         
26079         
26080     },
26081     alignment : {
26082         'left' : ['r-l', [-2,0], 'right'],
26083         'right' : ['l-r', [2,0], 'left'],
26084         'bottom' : ['t-b', [0,2], 'top'],
26085         'top' : [ 'b-t', [0,-2], 'bottom']
26086     }
26087     
26088 });
26089
26090
26091 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26092     
26093     
26094     bindEl : false,
26095     
26096     delay : null, // can be { show : 300 , hide: 500}
26097     
26098     timeout : null,
26099     
26100     hoverState : null, //???
26101     
26102     placement : 'bottom', 
26103     
26104     alignment : false,
26105     
26106     getAutoCreate : function(){
26107     
26108         var cfg = {
26109            cls : 'tooltip',
26110            role : 'tooltip',
26111            cn : [
26112                 {
26113                     cls : 'tooltip-arrow'
26114                 },
26115                 {
26116                     cls : 'tooltip-inner'
26117                 }
26118            ]
26119         };
26120         
26121         return cfg;
26122     },
26123     bind : function(el)
26124     {
26125         this.bindEl = el;
26126     },
26127       
26128     
26129     enter : function () {
26130        
26131         if (this.timeout != null) {
26132             clearTimeout(this.timeout);
26133         }
26134         
26135         this.hoverState = 'in';
26136          //Roo.log("enter - show");
26137         if (!this.delay || !this.delay.show) {
26138             this.show();
26139             return;
26140         }
26141         var _t = this;
26142         this.timeout = setTimeout(function () {
26143             if (_t.hoverState == 'in') {
26144                 _t.show();
26145             }
26146         }, this.delay.show);
26147     },
26148     leave : function()
26149     {
26150         clearTimeout(this.timeout);
26151     
26152         this.hoverState = 'out';
26153          if (!this.delay || !this.delay.hide) {
26154             this.hide();
26155             return;
26156         }
26157        
26158         var _t = this;
26159         this.timeout = setTimeout(function () {
26160             //Roo.log("leave - timeout");
26161             
26162             if (_t.hoverState == 'out') {
26163                 _t.hide();
26164                 Roo.bootstrap.Tooltip.currentEl = false;
26165             }
26166         }, delay);
26167     },
26168     
26169     show : function (msg)
26170     {
26171         if (!this.el) {
26172             this.render(document.body);
26173         }
26174         // set content.
26175         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26176         
26177         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26178         
26179         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26180         
26181         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26182         
26183         var placement = typeof this.placement == 'function' ?
26184             this.placement.call(this, this.el, on_el) :
26185             this.placement;
26186             
26187         var autoToken = /\s?auto?\s?/i;
26188         var autoPlace = autoToken.test(placement);
26189         if (autoPlace) {
26190             placement = placement.replace(autoToken, '') || 'top';
26191         }
26192         
26193         //this.el.detach()
26194         //this.el.setXY([0,0]);
26195         this.el.show();
26196         //this.el.dom.style.display='block';
26197         
26198         //this.el.appendTo(on_el);
26199         
26200         var p = this.getPosition();
26201         var box = this.el.getBox();
26202         
26203         if (autoPlace) {
26204             // fixme..
26205         }
26206         
26207         var align = this.alignment[placement];
26208         
26209         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26210         
26211         if(placement == 'top' || placement == 'bottom'){
26212             if(xy[0] < 0){
26213                 placement = 'right';
26214             }
26215             
26216             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26217                 placement = 'left';
26218             }
26219             
26220             var scroll = Roo.select('body', true).first().getScroll();
26221             
26222             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26223                 placement = 'top';
26224             }
26225             
26226             align = this.alignment[placement];
26227         }
26228         
26229         this.el.alignTo(this.bindEl, align[0],align[1]);
26230         //var arrow = this.el.select('.arrow',true).first();
26231         //arrow.set(align[2], 
26232         
26233         this.el.addClass(placement);
26234         
26235         this.el.addClass('in fade');
26236         
26237         this.hoverState = null;
26238         
26239         if (this.el.hasClass('fade')) {
26240             // fade it?
26241         }
26242         
26243     },
26244     hide : function()
26245     {
26246          
26247         if (!this.el) {
26248             return;
26249         }
26250         //this.el.setXY([0,0]);
26251         this.el.removeClass('in');
26252         //this.el.hide();
26253         
26254     }
26255     
26256 });
26257  
26258
26259  /*
26260  * - LGPL
26261  *
26262  * Location Picker
26263  * 
26264  */
26265
26266 /**
26267  * @class Roo.bootstrap.LocationPicker
26268  * @extends Roo.bootstrap.Component
26269  * Bootstrap LocationPicker class
26270  * @cfg {Number} latitude Position when init default 0
26271  * @cfg {Number} longitude Position when init default 0
26272  * @cfg {Number} zoom default 15
26273  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26274  * @cfg {Boolean} mapTypeControl default false
26275  * @cfg {Boolean} disableDoubleClickZoom default false
26276  * @cfg {Boolean} scrollwheel default true
26277  * @cfg {Boolean} streetViewControl default false
26278  * @cfg {Number} radius default 0
26279  * @cfg {String} locationName
26280  * @cfg {Boolean} draggable default true
26281  * @cfg {Boolean} enableAutocomplete default false
26282  * @cfg {Boolean} enableReverseGeocode default true
26283  * @cfg {String} markerTitle
26284  * 
26285  * @constructor
26286  * Create a new LocationPicker
26287  * @param {Object} config The config object
26288  */
26289
26290
26291 Roo.bootstrap.LocationPicker = function(config){
26292     
26293     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26294     
26295     this.addEvents({
26296         /**
26297          * @event initial
26298          * Fires when the picker initialized.
26299          * @param {Roo.bootstrap.LocationPicker} this
26300          * @param {Google Location} location
26301          */
26302         initial : true,
26303         /**
26304          * @event positionchanged
26305          * Fires when the picker position changed.
26306          * @param {Roo.bootstrap.LocationPicker} this
26307          * @param {Google Location} location
26308          */
26309         positionchanged : true,
26310         /**
26311          * @event resize
26312          * Fires when the map resize.
26313          * @param {Roo.bootstrap.LocationPicker} this
26314          */
26315         resize : true,
26316         /**
26317          * @event show
26318          * Fires when the map show.
26319          * @param {Roo.bootstrap.LocationPicker} this
26320          */
26321         show : true,
26322         /**
26323          * @event hide
26324          * Fires when the map hide.
26325          * @param {Roo.bootstrap.LocationPicker} this
26326          */
26327         hide : true,
26328         /**
26329          * @event mapClick
26330          * Fires when click the map.
26331          * @param {Roo.bootstrap.LocationPicker} this
26332          * @param {Map event} e
26333          */
26334         mapClick : true,
26335         /**
26336          * @event mapRightClick
26337          * Fires when right click the map.
26338          * @param {Roo.bootstrap.LocationPicker} this
26339          * @param {Map event} e
26340          */
26341         mapRightClick : true,
26342         /**
26343          * @event markerClick
26344          * Fires when click the marker.
26345          * @param {Roo.bootstrap.LocationPicker} this
26346          * @param {Map event} e
26347          */
26348         markerClick : true,
26349         /**
26350          * @event markerRightClick
26351          * Fires when right click the marker.
26352          * @param {Roo.bootstrap.LocationPicker} this
26353          * @param {Map event} e
26354          */
26355         markerRightClick : true,
26356         /**
26357          * @event OverlayViewDraw
26358          * Fires when OverlayView Draw
26359          * @param {Roo.bootstrap.LocationPicker} this
26360          */
26361         OverlayViewDraw : true,
26362         /**
26363          * @event OverlayViewOnAdd
26364          * Fires when OverlayView Draw
26365          * @param {Roo.bootstrap.LocationPicker} this
26366          */
26367         OverlayViewOnAdd : true,
26368         /**
26369          * @event OverlayViewOnRemove
26370          * Fires when OverlayView Draw
26371          * @param {Roo.bootstrap.LocationPicker} this
26372          */
26373         OverlayViewOnRemove : true,
26374         /**
26375          * @event OverlayViewShow
26376          * Fires when OverlayView Draw
26377          * @param {Roo.bootstrap.LocationPicker} this
26378          * @param {Pixel} cpx
26379          */
26380         OverlayViewShow : true,
26381         /**
26382          * @event OverlayViewHide
26383          * Fires when OverlayView Draw
26384          * @param {Roo.bootstrap.LocationPicker} this
26385          */
26386         OverlayViewHide : true,
26387         /**
26388          * @event loadexception
26389          * Fires when load google lib failed.
26390          * @param {Roo.bootstrap.LocationPicker} this
26391          */
26392         loadexception : true
26393     });
26394         
26395 };
26396
26397 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26398     
26399     gMapContext: false,
26400     
26401     latitude: 0,
26402     longitude: 0,
26403     zoom: 15,
26404     mapTypeId: false,
26405     mapTypeControl: false,
26406     disableDoubleClickZoom: false,
26407     scrollwheel: true,
26408     streetViewControl: false,
26409     radius: 0,
26410     locationName: '',
26411     draggable: true,
26412     enableAutocomplete: false,
26413     enableReverseGeocode: true,
26414     markerTitle: '',
26415     
26416     getAutoCreate: function()
26417     {
26418
26419         var cfg = {
26420             tag: 'div',
26421             cls: 'roo-location-picker'
26422         };
26423         
26424         return cfg
26425     },
26426     
26427     initEvents: function(ct, position)
26428     {       
26429         if(!this.el.getWidth() || this.isApplied()){
26430             return;
26431         }
26432         
26433         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26434         
26435         this.initial();
26436     },
26437     
26438     initial: function()
26439     {
26440         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26441             this.fireEvent('loadexception', this);
26442             return;
26443         }
26444         
26445         if(!this.mapTypeId){
26446             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26447         }
26448         
26449         this.gMapContext = this.GMapContext();
26450         
26451         this.initOverlayView();
26452         
26453         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26454         
26455         var _this = this;
26456                 
26457         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26458             _this.setPosition(_this.gMapContext.marker.position);
26459         });
26460         
26461         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26462             _this.fireEvent('mapClick', this, event);
26463             
26464         });
26465
26466         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26467             _this.fireEvent('mapRightClick', this, event);
26468             
26469         });
26470         
26471         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26472             _this.fireEvent('markerClick', this, event);
26473             
26474         });
26475
26476         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26477             _this.fireEvent('markerRightClick', this, event);
26478             
26479         });
26480         
26481         this.setPosition(this.gMapContext.location);
26482         
26483         this.fireEvent('initial', this, this.gMapContext.location);
26484     },
26485     
26486     initOverlayView: function()
26487     {
26488         var _this = this;
26489         
26490         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26491             
26492             draw: function()
26493             {
26494                 _this.fireEvent('OverlayViewDraw', _this);
26495             },
26496             
26497             onAdd: function()
26498             {
26499                 _this.fireEvent('OverlayViewOnAdd', _this);
26500             },
26501             
26502             onRemove: function()
26503             {
26504                 _this.fireEvent('OverlayViewOnRemove', _this);
26505             },
26506             
26507             show: function(cpx)
26508             {
26509                 _this.fireEvent('OverlayViewShow', _this, cpx);
26510             },
26511             
26512             hide: function()
26513             {
26514                 _this.fireEvent('OverlayViewHide', _this);
26515             }
26516             
26517         });
26518     },
26519     
26520     fromLatLngToContainerPixel: function(event)
26521     {
26522         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26523     },
26524     
26525     isApplied: function() 
26526     {
26527         return this.getGmapContext() == false ? false : true;
26528     },
26529     
26530     getGmapContext: function() 
26531     {
26532         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26533     },
26534     
26535     GMapContext: function() 
26536     {
26537         var position = new google.maps.LatLng(this.latitude, this.longitude);
26538         
26539         var _map = new google.maps.Map(this.el.dom, {
26540             center: position,
26541             zoom: this.zoom,
26542             mapTypeId: this.mapTypeId,
26543             mapTypeControl: this.mapTypeControl,
26544             disableDoubleClickZoom: this.disableDoubleClickZoom,
26545             scrollwheel: this.scrollwheel,
26546             streetViewControl: this.streetViewControl,
26547             locationName: this.locationName,
26548             draggable: this.draggable,
26549             enableAutocomplete: this.enableAutocomplete,
26550             enableReverseGeocode: this.enableReverseGeocode
26551         });
26552         
26553         var _marker = new google.maps.Marker({
26554             position: position,
26555             map: _map,
26556             title: this.markerTitle,
26557             draggable: this.draggable
26558         });
26559         
26560         return {
26561             map: _map,
26562             marker: _marker,
26563             circle: null,
26564             location: position,
26565             radius: this.radius,
26566             locationName: this.locationName,
26567             addressComponents: {
26568                 formatted_address: null,
26569                 addressLine1: null,
26570                 addressLine2: null,
26571                 streetName: null,
26572                 streetNumber: null,
26573                 city: null,
26574                 district: null,
26575                 state: null,
26576                 stateOrProvince: null
26577             },
26578             settings: this,
26579             domContainer: this.el.dom,
26580             geodecoder: new google.maps.Geocoder()
26581         };
26582     },
26583     
26584     drawCircle: function(center, radius, options) 
26585     {
26586         if (this.gMapContext.circle != null) {
26587             this.gMapContext.circle.setMap(null);
26588         }
26589         if (radius > 0) {
26590             radius *= 1;
26591             options = Roo.apply({}, options, {
26592                 strokeColor: "#0000FF",
26593                 strokeOpacity: .35,
26594                 strokeWeight: 2,
26595                 fillColor: "#0000FF",
26596                 fillOpacity: .2
26597             });
26598             
26599             options.map = this.gMapContext.map;
26600             options.radius = radius;
26601             options.center = center;
26602             this.gMapContext.circle = new google.maps.Circle(options);
26603             return this.gMapContext.circle;
26604         }
26605         
26606         return null;
26607     },
26608     
26609     setPosition: function(location) 
26610     {
26611         this.gMapContext.location = location;
26612         this.gMapContext.marker.setPosition(location);
26613         this.gMapContext.map.panTo(location);
26614         this.drawCircle(location, this.gMapContext.radius, {});
26615         
26616         var _this = this;
26617         
26618         if (this.gMapContext.settings.enableReverseGeocode) {
26619             this.gMapContext.geodecoder.geocode({
26620                 latLng: this.gMapContext.location
26621             }, function(results, status) {
26622                 
26623                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26624                     _this.gMapContext.locationName = results[0].formatted_address;
26625                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26626                     
26627                     _this.fireEvent('positionchanged', this, location);
26628                 }
26629             });
26630             
26631             return;
26632         }
26633         
26634         this.fireEvent('positionchanged', this, location);
26635     },
26636     
26637     resize: function()
26638     {
26639         google.maps.event.trigger(this.gMapContext.map, "resize");
26640         
26641         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26642         
26643         this.fireEvent('resize', this);
26644     },
26645     
26646     setPositionByLatLng: function(latitude, longitude)
26647     {
26648         this.setPosition(new google.maps.LatLng(latitude, longitude));
26649     },
26650     
26651     getCurrentPosition: function() 
26652     {
26653         return {
26654             latitude: this.gMapContext.location.lat(),
26655             longitude: this.gMapContext.location.lng()
26656         };
26657     },
26658     
26659     getAddressName: function() 
26660     {
26661         return this.gMapContext.locationName;
26662     },
26663     
26664     getAddressComponents: function() 
26665     {
26666         return this.gMapContext.addressComponents;
26667     },
26668     
26669     address_component_from_google_geocode: function(address_components) 
26670     {
26671         var result = {};
26672         
26673         for (var i = 0; i < address_components.length; i++) {
26674             var component = address_components[i];
26675             if (component.types.indexOf("postal_code") >= 0) {
26676                 result.postalCode = component.short_name;
26677             } else if (component.types.indexOf("street_number") >= 0) {
26678                 result.streetNumber = component.short_name;
26679             } else if (component.types.indexOf("route") >= 0) {
26680                 result.streetName = component.short_name;
26681             } else if (component.types.indexOf("neighborhood") >= 0) {
26682                 result.city = component.short_name;
26683             } else if (component.types.indexOf("locality") >= 0) {
26684                 result.city = component.short_name;
26685             } else if (component.types.indexOf("sublocality") >= 0) {
26686                 result.district = component.short_name;
26687             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26688                 result.stateOrProvince = component.short_name;
26689             } else if (component.types.indexOf("country") >= 0) {
26690                 result.country = component.short_name;
26691             }
26692         }
26693         
26694         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26695         result.addressLine2 = "";
26696         return result;
26697     },
26698     
26699     setZoomLevel: function(zoom)
26700     {
26701         this.gMapContext.map.setZoom(zoom);
26702     },
26703     
26704     show: function()
26705     {
26706         if(!this.el){
26707             return;
26708         }
26709         
26710         this.el.show();
26711         
26712         this.resize();
26713         
26714         this.fireEvent('show', this);
26715     },
26716     
26717     hide: function()
26718     {
26719         if(!this.el){
26720             return;
26721         }
26722         
26723         this.el.hide();
26724         
26725         this.fireEvent('hide', this);
26726     }
26727     
26728 });
26729
26730 Roo.apply(Roo.bootstrap.LocationPicker, {
26731     
26732     OverlayView : function(map, options)
26733     {
26734         options = options || {};
26735         
26736         this.setMap(map);
26737     }
26738     
26739     
26740 });/*
26741  * - LGPL
26742  *
26743  * Alert
26744  * 
26745  */
26746
26747 /**
26748  * @class Roo.bootstrap.Alert
26749  * @extends Roo.bootstrap.Component
26750  * Bootstrap Alert class
26751  * @cfg {String} title The title of alert
26752  * @cfg {String} html The content of alert
26753  * @cfg {String} weight (  success | info | warning | danger )
26754  * @cfg {String} faicon font-awesomeicon
26755  * 
26756  * @constructor
26757  * Create a new alert
26758  * @param {Object} config The config object
26759  */
26760
26761
26762 Roo.bootstrap.Alert = function(config){
26763     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26764     
26765 };
26766
26767 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26768     
26769     title: '',
26770     html: '',
26771     weight: false,
26772     faicon: false,
26773     
26774     getAutoCreate : function()
26775     {
26776         
26777         var cfg = {
26778             tag : 'div',
26779             cls : 'alert',
26780             cn : [
26781                 {
26782                     tag : 'i',
26783                     cls : 'roo-alert-icon'
26784                     
26785                 },
26786                 {
26787                     tag : 'b',
26788                     cls : 'roo-alert-title',
26789                     html : this.title
26790                 },
26791                 {
26792                     tag : 'span',
26793                     cls : 'roo-alert-text',
26794                     html : this.html
26795                 }
26796             ]
26797         };
26798         
26799         if(this.faicon){
26800             cfg.cn[0].cls += ' fa ' + this.faicon;
26801         }
26802         
26803         if(this.weight){
26804             cfg.cls += ' alert-' + this.weight;
26805         }
26806         
26807         return cfg;
26808     },
26809     
26810     initEvents: function() 
26811     {
26812         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26813     },
26814     
26815     setTitle : function(str)
26816     {
26817         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26818     },
26819     
26820     setText : function(str)
26821     {
26822         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26823     },
26824     
26825     setWeight : function(weight)
26826     {
26827         if(this.weight){
26828             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26829         }
26830         
26831         this.weight = weight;
26832         
26833         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26834     },
26835     
26836     setIcon : function(icon)
26837     {
26838         if(this.faicon){
26839             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26840         }
26841         
26842         this.faicon = icon;
26843         
26844         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26845     },
26846     
26847     hide: function() 
26848     {
26849         this.el.hide();   
26850     },
26851     
26852     show: function() 
26853     {  
26854         this.el.show();   
26855     }
26856     
26857 });
26858
26859  
26860 /*
26861 * Licence: LGPL
26862 */
26863
26864 /**
26865  * @class Roo.bootstrap.UploadCropbox
26866  * @extends Roo.bootstrap.Component
26867  * Bootstrap UploadCropbox class
26868  * @cfg {String} emptyText show when image has been loaded
26869  * @cfg {String} rotateNotify show when image too small to rotate
26870  * @cfg {Number} errorTimeout default 3000
26871  * @cfg {Number} minWidth default 300
26872  * @cfg {Number} minHeight default 300
26873  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26874  * @cfg {Boolean} isDocument (true|false) default false
26875  * @cfg {String} url action url
26876  * @cfg {String} paramName default 'imageUpload'
26877  * @cfg {String} method default POST
26878  * @cfg {Boolean} loadMask (true|false) default true
26879  * @cfg {Boolean} loadingText default 'Loading...'
26880  * 
26881  * @constructor
26882  * Create a new UploadCropbox
26883  * @param {Object} config The config object
26884  */
26885
26886 Roo.bootstrap.UploadCropbox = function(config){
26887     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26888     
26889     this.addEvents({
26890         /**
26891          * @event beforeselectfile
26892          * Fire before select file
26893          * @param {Roo.bootstrap.UploadCropbox} this
26894          */
26895         "beforeselectfile" : true,
26896         /**
26897          * @event initial
26898          * Fire after initEvent
26899          * @param {Roo.bootstrap.UploadCropbox} this
26900          */
26901         "initial" : true,
26902         /**
26903          * @event crop
26904          * Fire after initEvent
26905          * @param {Roo.bootstrap.UploadCropbox} this
26906          * @param {String} data
26907          */
26908         "crop" : true,
26909         /**
26910          * @event prepare
26911          * Fire when preparing the file data
26912          * @param {Roo.bootstrap.UploadCropbox} this
26913          * @param {Object} file
26914          */
26915         "prepare" : true,
26916         /**
26917          * @event exception
26918          * Fire when get exception
26919          * @param {Roo.bootstrap.UploadCropbox} this
26920          * @param {XMLHttpRequest} xhr
26921          */
26922         "exception" : true,
26923         /**
26924          * @event beforeloadcanvas
26925          * Fire before load the canvas
26926          * @param {Roo.bootstrap.UploadCropbox} this
26927          * @param {String} src
26928          */
26929         "beforeloadcanvas" : true,
26930         /**
26931          * @event trash
26932          * Fire when trash image
26933          * @param {Roo.bootstrap.UploadCropbox} this
26934          */
26935         "trash" : true,
26936         /**
26937          * @event download
26938          * Fire when download the image
26939          * @param {Roo.bootstrap.UploadCropbox} this
26940          */
26941         "download" : true,
26942         /**
26943          * @event footerbuttonclick
26944          * Fire when footerbuttonclick
26945          * @param {Roo.bootstrap.UploadCropbox} this
26946          * @param {String} type
26947          */
26948         "footerbuttonclick" : true,
26949         /**
26950          * @event resize
26951          * Fire when resize
26952          * @param {Roo.bootstrap.UploadCropbox} this
26953          */
26954         "resize" : true,
26955         /**
26956          * @event rotate
26957          * Fire when rotate the image
26958          * @param {Roo.bootstrap.UploadCropbox} this
26959          * @param {String} pos
26960          */
26961         "rotate" : true,
26962         /**
26963          * @event inspect
26964          * Fire when inspect the file
26965          * @param {Roo.bootstrap.UploadCropbox} this
26966          * @param {Object} file
26967          */
26968         "inspect" : true,
26969         /**
26970          * @event upload
26971          * Fire when xhr upload the file
26972          * @param {Roo.bootstrap.UploadCropbox} this
26973          * @param {Object} data
26974          */
26975         "upload" : true,
26976         /**
26977          * @event arrange
26978          * Fire when arrange the file data
26979          * @param {Roo.bootstrap.UploadCropbox} this
26980          * @param {Object} formData
26981          */
26982         "arrange" : true
26983     });
26984     
26985     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26986 };
26987
26988 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26989     
26990     emptyText : 'Click to upload image',
26991     rotateNotify : 'Image is too small to rotate',
26992     errorTimeout : 3000,
26993     scale : 0,
26994     baseScale : 1,
26995     rotate : 0,
26996     dragable : false,
26997     pinching : false,
26998     mouseX : 0,
26999     mouseY : 0,
27000     cropData : false,
27001     minWidth : 300,
27002     minHeight : 300,
27003     file : false,
27004     exif : {},
27005     baseRotate : 1,
27006     cropType : 'image/jpeg',
27007     buttons : false,
27008     canvasLoaded : false,
27009     isDocument : false,
27010     method : 'POST',
27011     paramName : 'imageUpload',
27012     loadMask : true,
27013     loadingText : 'Loading...',
27014     maskEl : false,
27015     
27016     getAutoCreate : function()
27017     {
27018         var cfg = {
27019             tag : 'div',
27020             cls : 'roo-upload-cropbox',
27021             cn : [
27022                 {
27023                     tag : 'input',
27024                     cls : 'roo-upload-cropbox-selector',
27025                     type : 'file'
27026                 },
27027                 {
27028                     tag : 'div',
27029                     cls : 'roo-upload-cropbox-body',
27030                     style : 'cursor:pointer',
27031                     cn : [
27032                         {
27033                             tag : 'div',
27034                             cls : 'roo-upload-cropbox-preview'
27035                         },
27036                         {
27037                             tag : 'div',
27038                             cls : 'roo-upload-cropbox-thumb'
27039                         },
27040                         {
27041                             tag : 'div',
27042                             cls : 'roo-upload-cropbox-empty-notify',
27043                             html : this.emptyText
27044                         },
27045                         {
27046                             tag : 'div',
27047                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27048                             html : this.rotateNotify
27049                         }
27050                     ]
27051                 },
27052                 {
27053                     tag : 'div',
27054                     cls : 'roo-upload-cropbox-footer',
27055                     cn : {
27056                         tag : 'div',
27057                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27058                         cn : []
27059                     }
27060                 }
27061             ]
27062         };
27063         
27064         return cfg;
27065     },
27066     
27067     onRender : function(ct, position)
27068     {
27069         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27070         
27071         if (this.buttons.length) {
27072             
27073             Roo.each(this.buttons, function(bb) {
27074                 
27075                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27076                 
27077                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27078                 
27079             }, this);
27080         }
27081         
27082         if(this.loadMask){
27083             this.maskEl = this.el;
27084         }
27085     },
27086     
27087     initEvents : function()
27088     {
27089         this.urlAPI = (window.createObjectURL && window) || 
27090                                 (window.URL && URL.revokeObjectURL && URL) || 
27091                                 (window.webkitURL && webkitURL);
27092                         
27093         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27094         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27095         
27096         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27097         this.selectorEl.hide();
27098         
27099         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27100         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27101         
27102         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27103         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27104         this.thumbEl.hide();
27105         
27106         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27107         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27108         
27109         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27110         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27111         this.errorEl.hide();
27112         
27113         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27114         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27115         this.footerEl.hide();
27116         
27117         this.setThumbBoxSize();
27118         
27119         this.bind();
27120         
27121         this.resize();
27122         
27123         this.fireEvent('initial', this);
27124     },
27125
27126     bind : function()
27127     {
27128         var _this = this;
27129         
27130         window.addEventListener("resize", function() { _this.resize(); } );
27131         
27132         this.bodyEl.on('click', this.beforeSelectFile, this);
27133         
27134         if(Roo.isTouch){
27135             this.bodyEl.on('touchstart', this.onTouchStart, this);
27136             this.bodyEl.on('touchmove', this.onTouchMove, this);
27137             this.bodyEl.on('touchend', this.onTouchEnd, this);
27138         }
27139         
27140         if(!Roo.isTouch){
27141             this.bodyEl.on('mousedown', this.onMouseDown, this);
27142             this.bodyEl.on('mousemove', this.onMouseMove, this);
27143             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27144             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27145             Roo.get(document).on('mouseup', this.onMouseUp, this);
27146         }
27147         
27148         this.selectorEl.on('change', this.onFileSelected, this);
27149     },
27150     
27151     reset : function()
27152     {    
27153         this.scale = 0;
27154         this.baseScale = 1;
27155         this.rotate = 0;
27156         this.baseRotate = 1;
27157         this.dragable = false;
27158         this.pinching = false;
27159         this.mouseX = 0;
27160         this.mouseY = 0;
27161         this.cropData = false;
27162         this.notifyEl.dom.innerHTML = this.emptyText;
27163         
27164         this.selectorEl.dom.value = '';
27165         
27166     },
27167     
27168     resize : function()
27169     {
27170         if(this.fireEvent('resize', this) != false){
27171             this.setThumbBoxPosition();
27172             this.setCanvasPosition();
27173         }
27174     },
27175     
27176     onFooterButtonClick : function(e, el, o, type)
27177     {
27178         switch (type) {
27179             case 'rotate-left' :
27180                 this.onRotateLeft(e);
27181                 break;
27182             case 'rotate-right' :
27183                 this.onRotateRight(e);
27184                 break;
27185             case 'picture' :
27186                 this.beforeSelectFile(e);
27187                 break;
27188             case 'trash' :
27189                 this.trash(e);
27190                 break;
27191             case 'crop' :
27192                 this.crop(e);
27193                 break;
27194             case 'download' :
27195                 this.download(e);
27196                 break;
27197             default :
27198                 break;
27199         }
27200         
27201         this.fireEvent('footerbuttonclick', this, type);
27202     },
27203     
27204     beforeSelectFile : function(e)
27205     {
27206         e.preventDefault();
27207         
27208         if(this.fireEvent('beforeselectfile', this) != false){
27209             this.selectorEl.dom.click();
27210         }
27211     },
27212     
27213     onFileSelected : function(e)
27214     {
27215         e.preventDefault();
27216         
27217         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27218             return;
27219         }
27220         
27221         var file = this.selectorEl.dom.files[0];
27222         
27223         if(this.fireEvent('inspect', this, file) != false){
27224             this.prepare(file);
27225         }
27226         
27227     },
27228     
27229     trash : function(e)
27230     {
27231         this.fireEvent('trash', this);
27232     },
27233     
27234     download : function(e)
27235     {
27236         this.fireEvent('download', this);
27237     },
27238     
27239     loadCanvas : function(src)
27240     {   
27241         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27242             
27243             this.reset();
27244             
27245             this.imageEl = document.createElement('img');
27246             
27247             var _this = this;
27248             
27249             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27250             
27251             this.imageEl.src = src;
27252         }
27253     },
27254     
27255     onLoadCanvas : function()
27256     {   
27257         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27258         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27259         
27260         this.bodyEl.un('click', this.beforeSelectFile, this);
27261         
27262         this.notifyEl.hide();
27263         this.thumbEl.show();
27264         this.footerEl.show();
27265         
27266         this.baseRotateLevel();
27267         
27268         if(this.isDocument){
27269             this.setThumbBoxSize();
27270         }
27271         
27272         this.setThumbBoxPosition();
27273         
27274         this.baseScaleLevel();
27275         
27276         this.draw();
27277         
27278         this.resize();
27279         
27280         this.canvasLoaded = true;
27281         
27282         if(this.loadMask){
27283             this.maskEl.unmask();
27284         }
27285         
27286     },
27287     
27288     setCanvasPosition : function()
27289     {   
27290         if(!this.canvasEl){
27291             return;
27292         }
27293         
27294         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27295         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27296         
27297         this.previewEl.setLeft(pw);
27298         this.previewEl.setTop(ph);
27299         
27300     },
27301     
27302     onMouseDown : function(e)
27303     {   
27304         e.stopEvent();
27305         
27306         this.dragable = true;
27307         this.pinching = false;
27308         
27309         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27310             this.dragable = false;
27311             return;
27312         }
27313         
27314         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27315         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27316         
27317     },
27318     
27319     onMouseMove : function(e)
27320     {   
27321         e.stopEvent();
27322         
27323         if(!this.canvasLoaded){
27324             return;
27325         }
27326         
27327         if (!this.dragable){
27328             return;
27329         }
27330         
27331         var minX = Math.ceil(this.thumbEl.getLeft(true));
27332         var minY = Math.ceil(this.thumbEl.getTop(true));
27333         
27334         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27335         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27336         
27337         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27338         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27339         
27340         x = x - this.mouseX;
27341         y = y - this.mouseY;
27342         
27343         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27344         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27345         
27346         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27347         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27348         
27349         this.previewEl.setLeft(bgX);
27350         this.previewEl.setTop(bgY);
27351         
27352         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27353         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27354     },
27355     
27356     onMouseUp : function(e)
27357     {   
27358         e.stopEvent();
27359         
27360         this.dragable = false;
27361     },
27362     
27363     onMouseWheel : function(e)
27364     {   
27365         e.stopEvent();
27366         
27367         this.startScale = this.scale;
27368         
27369         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27370         
27371         if(!this.zoomable()){
27372             this.scale = this.startScale;
27373             return;
27374         }
27375         
27376         this.draw();
27377         
27378         return;
27379     },
27380     
27381     zoomable : function()
27382     {
27383         var minScale = this.thumbEl.getWidth() / this.minWidth;
27384         
27385         if(this.minWidth < this.minHeight){
27386             minScale = this.thumbEl.getHeight() / this.minHeight;
27387         }
27388         
27389         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27390         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27391         
27392         if(
27393                 this.isDocument &&
27394                 (this.rotate == 0 || this.rotate == 180) && 
27395                 (
27396                     width > this.imageEl.OriginWidth || 
27397                     height > this.imageEl.OriginHeight ||
27398                     (width < this.minWidth && height < this.minHeight)
27399                 )
27400         ){
27401             return false;
27402         }
27403         
27404         if(
27405                 this.isDocument &&
27406                 (this.rotate == 90 || this.rotate == 270) && 
27407                 (
27408                     width > this.imageEl.OriginWidth || 
27409                     height > this.imageEl.OriginHeight ||
27410                     (width < this.minHeight && height < this.minWidth)
27411                 )
27412         ){
27413             return false;
27414         }
27415         
27416         if(
27417                 !this.isDocument &&
27418                 (this.rotate == 0 || this.rotate == 180) && 
27419                 (
27420                     width < this.minWidth || 
27421                     width > this.imageEl.OriginWidth || 
27422                     height < this.minHeight || 
27423                     height > this.imageEl.OriginHeight
27424                 )
27425         ){
27426             return false;
27427         }
27428         
27429         if(
27430                 !this.isDocument &&
27431                 (this.rotate == 90 || this.rotate == 270) && 
27432                 (
27433                     width < this.minHeight || 
27434                     width > this.imageEl.OriginWidth || 
27435                     height < this.minWidth || 
27436                     height > this.imageEl.OriginHeight
27437                 )
27438         ){
27439             return false;
27440         }
27441         
27442         return true;
27443         
27444     },
27445     
27446     onRotateLeft : function(e)
27447     {   
27448         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27449             
27450             var minScale = this.thumbEl.getWidth() / this.minWidth;
27451             
27452             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27453             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27454             
27455             this.startScale = this.scale;
27456             
27457             while (this.getScaleLevel() < minScale){
27458             
27459                 this.scale = this.scale + 1;
27460                 
27461                 if(!this.zoomable()){
27462                     break;
27463                 }
27464                 
27465                 if(
27466                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27467                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27468                 ){
27469                     continue;
27470                 }
27471                 
27472                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27473
27474                 this.draw();
27475                 
27476                 return;
27477             }
27478             
27479             this.scale = this.startScale;
27480             
27481             this.onRotateFail();
27482             
27483             return false;
27484         }
27485         
27486         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27487
27488         if(this.isDocument){
27489             this.setThumbBoxSize();
27490             this.setThumbBoxPosition();
27491             this.setCanvasPosition();
27492         }
27493         
27494         this.draw();
27495         
27496         this.fireEvent('rotate', this, 'left');
27497         
27498     },
27499     
27500     onRotateRight : function(e)
27501     {
27502         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27503             
27504             var minScale = this.thumbEl.getWidth() / this.minWidth;
27505         
27506             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27507             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27508             
27509             this.startScale = this.scale;
27510             
27511             while (this.getScaleLevel() < minScale){
27512             
27513                 this.scale = this.scale + 1;
27514                 
27515                 if(!this.zoomable()){
27516                     break;
27517                 }
27518                 
27519                 if(
27520                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27521                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27522                 ){
27523                     continue;
27524                 }
27525                 
27526                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27527
27528                 this.draw();
27529                 
27530                 return;
27531             }
27532             
27533             this.scale = this.startScale;
27534             
27535             this.onRotateFail();
27536             
27537             return false;
27538         }
27539         
27540         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27541
27542         if(this.isDocument){
27543             this.setThumbBoxSize();
27544             this.setThumbBoxPosition();
27545             this.setCanvasPosition();
27546         }
27547         
27548         this.draw();
27549         
27550         this.fireEvent('rotate', this, 'right');
27551     },
27552     
27553     onRotateFail : function()
27554     {
27555         this.errorEl.show(true);
27556         
27557         var _this = this;
27558         
27559         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27560     },
27561     
27562     draw : function()
27563     {
27564         this.previewEl.dom.innerHTML = '';
27565         
27566         var canvasEl = document.createElement("canvas");
27567         
27568         var contextEl = canvasEl.getContext("2d");
27569         
27570         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27571         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27572         var center = this.imageEl.OriginWidth / 2;
27573         
27574         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27575             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27576             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27577             center = this.imageEl.OriginHeight / 2;
27578         }
27579         
27580         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27581         
27582         contextEl.translate(center, center);
27583         contextEl.rotate(this.rotate * Math.PI / 180);
27584
27585         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27586         
27587         this.canvasEl = document.createElement("canvas");
27588         
27589         this.contextEl = this.canvasEl.getContext("2d");
27590         
27591         switch (this.rotate) {
27592             case 0 :
27593                 
27594                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27595                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
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 90 : 
27601                 
27602                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27603                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27604                 
27605                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27606                     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);
27607                     break;
27608                 }
27609                 
27610                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27611                 
27612                 break;
27613             case 180 :
27614                 
27615                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27616                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27617                 
27618                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27619                     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);
27620                     break;
27621                 }
27622                 
27623                 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);
27624                 
27625                 break;
27626             case 270 :
27627                 
27628                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27629                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27630         
27631                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27632                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27633                     break;
27634                 }
27635                 
27636                 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);
27637                 
27638                 break;
27639             default : 
27640                 break;
27641         }
27642         
27643         this.previewEl.appendChild(this.canvasEl);
27644         
27645         this.setCanvasPosition();
27646     },
27647     
27648     crop : function()
27649     {
27650         if(!this.canvasLoaded){
27651             return;
27652         }
27653         
27654         var imageCanvas = document.createElement("canvas");
27655         
27656         var imageContext = imageCanvas.getContext("2d");
27657         
27658         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27659         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27660         
27661         var center = imageCanvas.width / 2;
27662         
27663         imageContext.translate(center, center);
27664         
27665         imageContext.rotate(this.rotate * Math.PI / 180);
27666         
27667         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27668         
27669         var canvas = document.createElement("canvas");
27670         
27671         var context = canvas.getContext("2d");
27672                 
27673         canvas.width = this.minWidth;
27674         canvas.height = this.minHeight;
27675
27676         switch (this.rotate) {
27677             case 0 :
27678                 
27679                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27680                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27681                 
27682                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27683                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27684                 
27685                 var targetWidth = this.minWidth - 2 * x;
27686                 var targetHeight = this.minHeight - 2 * y;
27687                 
27688                 var scale = 1;
27689                 
27690                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27691                     scale = targetWidth / width;
27692                 }
27693                 
27694                 if(x > 0 && y == 0){
27695                     scale = targetHeight / height;
27696                 }
27697                 
27698                 if(x > 0 && y > 0){
27699                     scale = targetWidth / width;
27700                     
27701                     if(width < height){
27702                         scale = targetHeight / height;
27703                     }
27704                 }
27705                 
27706                 context.scale(scale, scale);
27707                 
27708                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27709                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27710
27711                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27712                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27713
27714                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27715                 
27716                 break;
27717             case 90 : 
27718                 
27719                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27720                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27721                 
27722                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27723                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27724                 
27725                 var targetWidth = this.minWidth - 2 * x;
27726                 var targetHeight = this.minHeight - 2 * y;
27727                 
27728                 var scale = 1;
27729                 
27730                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27731                     scale = targetWidth / width;
27732                 }
27733                 
27734                 if(x > 0 && y == 0){
27735                     scale = targetHeight / height;
27736                 }
27737                 
27738                 if(x > 0 && y > 0){
27739                     scale = targetWidth / width;
27740                     
27741                     if(width < height){
27742                         scale = targetHeight / height;
27743                     }
27744                 }
27745                 
27746                 context.scale(scale, scale);
27747                 
27748                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27749                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27750
27751                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27752                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27753                 
27754                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27755                 
27756                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27757                 
27758                 break;
27759             case 180 :
27760                 
27761                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27762                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27763                 
27764                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27765                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27766                 
27767                 var targetWidth = this.minWidth - 2 * x;
27768                 var targetHeight = this.minHeight - 2 * y;
27769                 
27770                 var scale = 1;
27771                 
27772                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27773                     scale = targetWidth / width;
27774                 }
27775                 
27776                 if(x > 0 && y == 0){
27777                     scale = targetHeight / height;
27778                 }
27779                 
27780                 if(x > 0 && y > 0){
27781                     scale = targetWidth / width;
27782                     
27783                     if(width < height){
27784                         scale = targetHeight / height;
27785                     }
27786                 }
27787                 
27788                 context.scale(scale, scale);
27789                 
27790                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27791                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27792
27793                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27794                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27795
27796                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27797                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27798                 
27799                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27800                 
27801                 break;
27802             case 270 :
27803                 
27804                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27805                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27806                 
27807                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27808                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27809                 
27810                 var targetWidth = this.minWidth - 2 * x;
27811                 var targetHeight = this.minHeight - 2 * y;
27812                 
27813                 var scale = 1;
27814                 
27815                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27816                     scale = targetWidth / width;
27817                 }
27818                 
27819                 if(x > 0 && y == 0){
27820                     scale = targetHeight / height;
27821                 }
27822                 
27823                 if(x > 0 && y > 0){
27824                     scale = targetWidth / width;
27825                     
27826                     if(width < height){
27827                         scale = targetHeight / height;
27828                     }
27829                 }
27830                 
27831                 context.scale(scale, scale);
27832                 
27833                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27834                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27835
27836                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27837                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27838                 
27839                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27840                 
27841                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27842                 
27843                 break;
27844             default : 
27845                 break;
27846         }
27847         
27848         this.cropData = canvas.toDataURL(this.cropType);
27849         
27850         if(this.fireEvent('crop', this, this.cropData) !== false){
27851             this.process(this.file, this.cropData);
27852         }
27853         
27854         return;
27855         
27856     },
27857     
27858     setThumbBoxSize : function()
27859     {
27860         var width, height;
27861         
27862         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27863             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27864             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27865             
27866             this.minWidth = width;
27867             this.minHeight = height;
27868             
27869             if(this.rotate == 90 || this.rotate == 270){
27870                 this.minWidth = height;
27871                 this.minHeight = width;
27872             }
27873         }
27874         
27875         height = 300;
27876         width = Math.ceil(this.minWidth * height / this.minHeight);
27877         
27878         if(this.minWidth > this.minHeight){
27879             width = 300;
27880             height = Math.ceil(this.minHeight * width / this.minWidth);
27881         }
27882         
27883         this.thumbEl.setStyle({
27884             width : width + 'px',
27885             height : height + 'px'
27886         });
27887
27888         return;
27889             
27890     },
27891     
27892     setThumbBoxPosition : function()
27893     {
27894         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27895         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27896         
27897         this.thumbEl.setLeft(x);
27898         this.thumbEl.setTop(y);
27899         
27900     },
27901     
27902     baseRotateLevel : function()
27903     {
27904         this.baseRotate = 1;
27905         
27906         if(
27907                 typeof(this.exif) != 'undefined' &&
27908                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27909                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27910         ){
27911             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27912         }
27913         
27914         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27915         
27916     },
27917     
27918     baseScaleLevel : function()
27919     {
27920         var width, height;
27921         
27922         if(this.isDocument){
27923             
27924             if(this.baseRotate == 6 || this.baseRotate == 8){
27925             
27926                 height = this.thumbEl.getHeight();
27927                 this.baseScale = height / this.imageEl.OriginWidth;
27928
27929                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27930                     width = this.thumbEl.getWidth();
27931                     this.baseScale = width / this.imageEl.OriginHeight;
27932                 }
27933
27934                 return;
27935             }
27936
27937             height = this.thumbEl.getHeight();
27938             this.baseScale = height / this.imageEl.OriginHeight;
27939
27940             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27941                 width = this.thumbEl.getWidth();
27942                 this.baseScale = width / this.imageEl.OriginWidth;
27943             }
27944
27945             return;
27946         }
27947         
27948         if(this.baseRotate == 6 || this.baseRotate == 8){
27949             
27950             width = this.thumbEl.getHeight();
27951             this.baseScale = width / this.imageEl.OriginHeight;
27952             
27953             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27954                 height = this.thumbEl.getWidth();
27955                 this.baseScale = height / this.imageEl.OriginHeight;
27956             }
27957             
27958             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27959                 height = this.thumbEl.getWidth();
27960                 this.baseScale = height / this.imageEl.OriginHeight;
27961                 
27962                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27963                     width = this.thumbEl.getHeight();
27964                     this.baseScale = width / this.imageEl.OriginWidth;
27965                 }
27966             }
27967             
27968             return;
27969         }
27970         
27971         width = this.thumbEl.getWidth();
27972         this.baseScale = width / this.imageEl.OriginWidth;
27973         
27974         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27975             height = this.thumbEl.getHeight();
27976             this.baseScale = height / this.imageEl.OriginHeight;
27977         }
27978         
27979         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27980             
27981             height = this.thumbEl.getHeight();
27982             this.baseScale = height / this.imageEl.OriginHeight;
27983             
27984             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27985                 width = this.thumbEl.getWidth();
27986                 this.baseScale = width / this.imageEl.OriginWidth;
27987             }
27988             
27989         }
27990         
27991         return;
27992     },
27993     
27994     getScaleLevel : function()
27995     {
27996         return this.baseScale * Math.pow(1.1, this.scale);
27997     },
27998     
27999     onTouchStart : function(e)
28000     {
28001         if(!this.canvasLoaded){
28002             this.beforeSelectFile(e);
28003             return;
28004         }
28005         
28006         var touches = e.browserEvent.touches;
28007         
28008         if(!touches){
28009             return;
28010         }
28011         
28012         if(touches.length == 1){
28013             this.onMouseDown(e);
28014             return;
28015         }
28016         
28017         if(touches.length != 2){
28018             return;
28019         }
28020         
28021         var coords = [];
28022         
28023         for(var i = 0, finger; finger = touches[i]; i++){
28024             coords.push(finger.pageX, finger.pageY);
28025         }
28026         
28027         var x = Math.pow(coords[0] - coords[2], 2);
28028         var y = Math.pow(coords[1] - coords[3], 2);
28029         
28030         this.startDistance = Math.sqrt(x + y);
28031         
28032         this.startScale = this.scale;
28033         
28034         this.pinching = true;
28035         this.dragable = false;
28036         
28037     },
28038     
28039     onTouchMove : function(e)
28040     {
28041         if(!this.pinching && !this.dragable){
28042             return;
28043         }
28044         
28045         var touches = e.browserEvent.touches;
28046         
28047         if(!touches){
28048             return;
28049         }
28050         
28051         if(this.dragable){
28052             this.onMouseMove(e);
28053             return;
28054         }
28055         
28056         var coords = [];
28057         
28058         for(var i = 0, finger; finger = touches[i]; i++){
28059             coords.push(finger.pageX, finger.pageY);
28060         }
28061         
28062         var x = Math.pow(coords[0] - coords[2], 2);
28063         var y = Math.pow(coords[1] - coords[3], 2);
28064         
28065         this.endDistance = Math.sqrt(x + y);
28066         
28067         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28068         
28069         if(!this.zoomable()){
28070             this.scale = this.startScale;
28071             return;
28072         }
28073         
28074         this.draw();
28075         
28076     },
28077     
28078     onTouchEnd : function(e)
28079     {
28080         this.pinching = false;
28081         this.dragable = false;
28082         
28083     },
28084     
28085     process : function(file, crop)
28086     {
28087         if(this.loadMask){
28088             this.maskEl.mask(this.loadingText);
28089         }
28090         
28091         this.xhr = new XMLHttpRequest();
28092         
28093         file.xhr = this.xhr;
28094
28095         this.xhr.open(this.method, this.url, true);
28096         
28097         var headers = {
28098             "Accept": "application/json",
28099             "Cache-Control": "no-cache",
28100             "X-Requested-With": "XMLHttpRequest"
28101         };
28102         
28103         for (var headerName in headers) {
28104             var headerValue = headers[headerName];
28105             if (headerValue) {
28106                 this.xhr.setRequestHeader(headerName, headerValue);
28107             }
28108         }
28109         
28110         var _this = this;
28111         
28112         this.xhr.onload = function()
28113         {
28114             _this.xhrOnLoad(_this.xhr);
28115         }
28116         
28117         this.xhr.onerror = function()
28118         {
28119             _this.xhrOnError(_this.xhr);
28120         }
28121         
28122         var formData = new FormData();
28123
28124         formData.append('returnHTML', 'NO');
28125         
28126         if(crop){
28127             formData.append('crop', crop);
28128         }
28129         
28130         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28131             formData.append(this.paramName, file, file.name);
28132         }
28133         
28134         if(typeof(file.filename) != 'undefined'){
28135             formData.append('filename', file.filename);
28136         }
28137         
28138         if(typeof(file.mimetype) != 'undefined'){
28139             formData.append('mimetype', file.mimetype);
28140         }
28141         
28142         if(this.fireEvent('arrange', this, formData) != false){
28143             this.xhr.send(formData);
28144         };
28145     },
28146     
28147     xhrOnLoad : function(xhr)
28148     {
28149         if(this.loadMask){
28150             this.maskEl.unmask();
28151         }
28152         
28153         if (xhr.readyState !== 4) {
28154             this.fireEvent('exception', this, xhr);
28155             return;
28156         }
28157
28158         var response = Roo.decode(xhr.responseText);
28159         
28160         if(!response.success){
28161             this.fireEvent('exception', this, xhr);
28162             return;
28163         }
28164         
28165         var response = Roo.decode(xhr.responseText);
28166         
28167         this.fireEvent('upload', this, response);
28168         
28169     },
28170     
28171     xhrOnError : function()
28172     {
28173         if(this.loadMask){
28174             this.maskEl.unmask();
28175         }
28176         
28177         Roo.log('xhr on error');
28178         
28179         var response = Roo.decode(xhr.responseText);
28180           
28181         Roo.log(response);
28182         
28183     },
28184     
28185     prepare : function(file)
28186     {   
28187         if(this.loadMask){
28188             this.maskEl.mask(this.loadingText);
28189         }
28190         
28191         this.file = false;
28192         this.exif = {};
28193         
28194         if(typeof(file) === 'string'){
28195             this.loadCanvas(file);
28196             return;
28197         }
28198         
28199         if(!file || !this.urlAPI){
28200             return;
28201         }
28202         
28203         this.file = file;
28204         this.cropType = file.type;
28205         
28206         var _this = this;
28207         
28208         if(this.fireEvent('prepare', this, this.file) != false){
28209             
28210             var reader = new FileReader();
28211             
28212             reader.onload = function (e) {
28213                 if (e.target.error) {
28214                     Roo.log(e.target.error);
28215                     return;
28216                 }
28217                 
28218                 var buffer = e.target.result,
28219                     dataView = new DataView(buffer),
28220                     offset = 2,
28221                     maxOffset = dataView.byteLength - 4,
28222                     markerBytes,
28223                     markerLength;
28224                 
28225                 if (dataView.getUint16(0) === 0xffd8) {
28226                     while (offset < maxOffset) {
28227                         markerBytes = dataView.getUint16(offset);
28228                         
28229                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28230                             markerLength = dataView.getUint16(offset + 2) + 2;
28231                             if (offset + markerLength > dataView.byteLength) {
28232                                 Roo.log('Invalid meta data: Invalid segment size.');
28233                                 break;
28234                             }
28235                             
28236                             if(markerBytes == 0xffe1){
28237                                 _this.parseExifData(
28238                                     dataView,
28239                                     offset,
28240                                     markerLength
28241                                 );
28242                             }
28243                             
28244                             offset += markerLength;
28245                             
28246                             continue;
28247                         }
28248                         
28249                         break;
28250                     }
28251                     
28252                 }
28253                 
28254                 var url = _this.urlAPI.createObjectURL(_this.file);
28255                 
28256                 _this.loadCanvas(url);
28257                 
28258                 return;
28259             }
28260             
28261             reader.readAsArrayBuffer(this.file);
28262             
28263         }
28264         
28265     },
28266     
28267     parseExifData : function(dataView, offset, length)
28268     {
28269         var tiffOffset = offset + 10,
28270             littleEndian,
28271             dirOffset;
28272     
28273         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28274             // No Exif data, might be XMP data instead
28275             return;
28276         }
28277         
28278         // Check for the ASCII code for "Exif" (0x45786966):
28279         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28280             // No Exif data, might be XMP data instead
28281             return;
28282         }
28283         if (tiffOffset + 8 > dataView.byteLength) {
28284             Roo.log('Invalid Exif data: Invalid segment size.');
28285             return;
28286         }
28287         // Check for the two null bytes:
28288         if (dataView.getUint16(offset + 8) !== 0x0000) {
28289             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28290             return;
28291         }
28292         // Check the byte alignment:
28293         switch (dataView.getUint16(tiffOffset)) {
28294         case 0x4949:
28295             littleEndian = true;
28296             break;
28297         case 0x4D4D:
28298             littleEndian = false;
28299             break;
28300         default:
28301             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28302             return;
28303         }
28304         // Check for the TIFF tag marker (0x002A):
28305         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28306             Roo.log('Invalid Exif data: Missing TIFF marker.');
28307             return;
28308         }
28309         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28310         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28311         
28312         this.parseExifTags(
28313             dataView,
28314             tiffOffset,
28315             tiffOffset + dirOffset,
28316             littleEndian
28317         );
28318     },
28319     
28320     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28321     {
28322         var tagsNumber,
28323             dirEndOffset,
28324             i;
28325         if (dirOffset + 6 > dataView.byteLength) {
28326             Roo.log('Invalid Exif data: Invalid directory offset.');
28327             return;
28328         }
28329         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28330         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28331         if (dirEndOffset + 4 > dataView.byteLength) {
28332             Roo.log('Invalid Exif data: Invalid directory size.');
28333             return;
28334         }
28335         for (i = 0; i < tagsNumber; i += 1) {
28336             this.parseExifTag(
28337                 dataView,
28338                 tiffOffset,
28339                 dirOffset + 2 + 12 * i, // tag offset
28340                 littleEndian
28341             );
28342         }
28343         // Return the offset to the next directory:
28344         return dataView.getUint32(dirEndOffset, littleEndian);
28345     },
28346     
28347     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28348     {
28349         var tag = dataView.getUint16(offset, littleEndian);
28350         
28351         this.exif[tag] = this.getExifValue(
28352             dataView,
28353             tiffOffset,
28354             offset,
28355             dataView.getUint16(offset + 2, littleEndian), // tag type
28356             dataView.getUint32(offset + 4, littleEndian), // tag length
28357             littleEndian
28358         );
28359     },
28360     
28361     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28362     {
28363         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28364             tagSize,
28365             dataOffset,
28366             values,
28367             i,
28368             str,
28369             c;
28370     
28371         if (!tagType) {
28372             Roo.log('Invalid Exif data: Invalid tag type.');
28373             return;
28374         }
28375         
28376         tagSize = tagType.size * length;
28377         // Determine if the value is contained in the dataOffset bytes,
28378         // or if the value at the dataOffset is a pointer to the actual data:
28379         dataOffset = tagSize > 4 ?
28380                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28381         if (dataOffset + tagSize > dataView.byteLength) {
28382             Roo.log('Invalid Exif data: Invalid data offset.');
28383             return;
28384         }
28385         if (length === 1) {
28386             return tagType.getValue(dataView, dataOffset, littleEndian);
28387         }
28388         values = [];
28389         for (i = 0; i < length; i += 1) {
28390             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28391         }
28392         
28393         if (tagType.ascii) {
28394             str = '';
28395             // Concatenate the chars:
28396             for (i = 0; i < values.length; i += 1) {
28397                 c = values[i];
28398                 // Ignore the terminating NULL byte(s):
28399                 if (c === '\u0000') {
28400                     break;
28401                 }
28402                 str += c;
28403             }
28404             return str;
28405         }
28406         return values;
28407     }
28408     
28409 });
28410
28411 Roo.apply(Roo.bootstrap.UploadCropbox, {
28412     tags : {
28413         'Orientation': 0x0112
28414     },
28415     
28416     Orientation: {
28417             1: 0, //'top-left',
28418 //            2: 'top-right',
28419             3: 180, //'bottom-right',
28420 //            4: 'bottom-left',
28421 //            5: 'left-top',
28422             6: 90, //'right-top',
28423 //            7: 'right-bottom',
28424             8: 270 //'left-bottom'
28425     },
28426     
28427     exifTagTypes : {
28428         // byte, 8-bit unsigned int:
28429         1: {
28430             getValue: function (dataView, dataOffset) {
28431                 return dataView.getUint8(dataOffset);
28432             },
28433             size: 1
28434         },
28435         // ascii, 8-bit byte:
28436         2: {
28437             getValue: function (dataView, dataOffset) {
28438                 return String.fromCharCode(dataView.getUint8(dataOffset));
28439             },
28440             size: 1,
28441             ascii: true
28442         },
28443         // short, 16 bit int:
28444         3: {
28445             getValue: function (dataView, dataOffset, littleEndian) {
28446                 return dataView.getUint16(dataOffset, littleEndian);
28447             },
28448             size: 2
28449         },
28450         // long, 32 bit int:
28451         4: {
28452             getValue: function (dataView, dataOffset, littleEndian) {
28453                 return dataView.getUint32(dataOffset, littleEndian);
28454             },
28455             size: 4
28456         },
28457         // rational = two long values, first is numerator, second is denominator:
28458         5: {
28459             getValue: function (dataView, dataOffset, littleEndian) {
28460                 return dataView.getUint32(dataOffset, littleEndian) /
28461                     dataView.getUint32(dataOffset + 4, littleEndian);
28462             },
28463             size: 8
28464         },
28465         // slong, 32 bit signed int:
28466         9: {
28467             getValue: function (dataView, dataOffset, littleEndian) {
28468                 return dataView.getInt32(dataOffset, littleEndian);
28469             },
28470             size: 4
28471         },
28472         // srational, two slongs, first is numerator, second is denominator:
28473         10: {
28474             getValue: function (dataView, dataOffset, littleEndian) {
28475                 return dataView.getInt32(dataOffset, littleEndian) /
28476                     dataView.getInt32(dataOffset + 4, littleEndian);
28477             },
28478             size: 8
28479         }
28480     },
28481     
28482     footer : {
28483         STANDARD : [
28484             {
28485                 tag : 'div',
28486                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28487                 action : 'rotate-left',
28488                 cn : [
28489                     {
28490                         tag : 'button',
28491                         cls : 'btn btn-default',
28492                         html : '<i class="fa fa-undo"></i>'
28493                     }
28494                 ]
28495             },
28496             {
28497                 tag : 'div',
28498                 cls : 'btn-group roo-upload-cropbox-picture',
28499                 action : 'picture',
28500                 cn : [
28501                     {
28502                         tag : 'button',
28503                         cls : 'btn btn-default',
28504                         html : '<i class="fa fa-picture-o"></i>'
28505                     }
28506                 ]
28507             },
28508             {
28509                 tag : 'div',
28510                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28511                 action : 'rotate-right',
28512                 cn : [
28513                     {
28514                         tag : 'button',
28515                         cls : 'btn btn-default',
28516                         html : '<i class="fa fa-repeat"></i>'
28517                     }
28518                 ]
28519             }
28520         ],
28521         DOCUMENT : [
28522             {
28523                 tag : 'div',
28524                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28525                 action : 'rotate-left',
28526                 cn : [
28527                     {
28528                         tag : 'button',
28529                         cls : 'btn btn-default',
28530                         html : '<i class="fa fa-undo"></i>'
28531                     }
28532                 ]
28533             },
28534             {
28535                 tag : 'div',
28536                 cls : 'btn-group roo-upload-cropbox-download',
28537                 action : 'download',
28538                 cn : [
28539                     {
28540                         tag : 'button',
28541                         cls : 'btn btn-default',
28542                         html : '<i class="fa fa-download"></i>'
28543                     }
28544                 ]
28545             },
28546             {
28547                 tag : 'div',
28548                 cls : 'btn-group roo-upload-cropbox-crop',
28549                 action : 'crop',
28550                 cn : [
28551                     {
28552                         tag : 'button',
28553                         cls : 'btn btn-default',
28554                         html : '<i class="fa fa-crop"></i>'
28555                     }
28556                 ]
28557             },
28558             {
28559                 tag : 'div',
28560                 cls : 'btn-group roo-upload-cropbox-trash',
28561                 action : 'trash',
28562                 cn : [
28563                     {
28564                         tag : 'button',
28565                         cls : 'btn btn-default',
28566                         html : '<i class="fa fa-trash"></i>'
28567                     }
28568                 ]
28569             },
28570             {
28571                 tag : 'div',
28572                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28573                 action : 'rotate-right',
28574                 cn : [
28575                     {
28576                         tag : 'button',
28577                         cls : 'btn btn-default',
28578                         html : '<i class="fa fa-repeat"></i>'
28579                     }
28580                 ]
28581             }
28582         ],
28583         ROTATOR : [
28584             {
28585                 tag : 'div',
28586                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28587                 action : 'rotate-left',
28588                 cn : [
28589                     {
28590                         tag : 'button',
28591                         cls : 'btn btn-default',
28592                         html : '<i class="fa fa-undo"></i>'
28593                     }
28594                 ]
28595             },
28596             {
28597                 tag : 'div',
28598                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28599                 action : 'rotate-right',
28600                 cn : [
28601                     {
28602                         tag : 'button',
28603                         cls : 'btn btn-default',
28604                         html : '<i class="fa fa-repeat"></i>'
28605                     }
28606                 ]
28607             }
28608         ]
28609     }
28610 });
28611
28612 /*
28613 * Licence: LGPL
28614 */
28615
28616 /**
28617  * @class Roo.bootstrap.DocumentManager
28618  * @extends Roo.bootstrap.Component
28619  * Bootstrap DocumentManager class
28620  * @cfg {String} paramName default 'imageUpload'
28621  * @cfg {String} toolTipName default 'filename'
28622  * @cfg {String} method default POST
28623  * @cfg {String} url action url
28624  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28625  * @cfg {Boolean} multiple multiple upload default true
28626  * @cfg {Number} thumbSize default 300
28627  * @cfg {String} fieldLabel
28628  * @cfg {Number} labelWidth default 4
28629  * @cfg {String} labelAlign (left|top) default left
28630  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28631 * @cfg {Number} labellg set the width of label (1-12)
28632  * @cfg {Number} labelmd set the width of label (1-12)
28633  * @cfg {Number} labelsm set the width of label (1-12)
28634  * @cfg {Number} labelxs set the width of label (1-12)
28635  * 
28636  * @constructor
28637  * Create a new DocumentManager
28638  * @param {Object} config The config object
28639  */
28640
28641 Roo.bootstrap.DocumentManager = function(config){
28642     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28643     
28644     this.files = [];
28645     this.delegates = [];
28646     
28647     this.addEvents({
28648         /**
28649          * @event initial
28650          * Fire when initial the DocumentManager
28651          * @param {Roo.bootstrap.DocumentManager} this
28652          */
28653         "initial" : true,
28654         /**
28655          * @event inspect
28656          * inspect selected file
28657          * @param {Roo.bootstrap.DocumentManager} this
28658          * @param {File} file
28659          */
28660         "inspect" : true,
28661         /**
28662          * @event exception
28663          * Fire when xhr load exception
28664          * @param {Roo.bootstrap.DocumentManager} this
28665          * @param {XMLHttpRequest} xhr
28666          */
28667         "exception" : true,
28668         /**
28669          * @event afterupload
28670          * Fire when xhr load exception
28671          * @param {Roo.bootstrap.DocumentManager} this
28672          * @param {XMLHttpRequest} xhr
28673          */
28674         "afterupload" : true,
28675         /**
28676          * @event prepare
28677          * prepare the form data
28678          * @param {Roo.bootstrap.DocumentManager} this
28679          * @param {Object} formData
28680          */
28681         "prepare" : true,
28682         /**
28683          * @event remove
28684          * Fire when remove the file
28685          * @param {Roo.bootstrap.DocumentManager} this
28686          * @param {Object} file
28687          */
28688         "remove" : true,
28689         /**
28690          * @event refresh
28691          * Fire after refresh the file
28692          * @param {Roo.bootstrap.DocumentManager} this
28693          */
28694         "refresh" : true,
28695         /**
28696          * @event click
28697          * Fire after click the image
28698          * @param {Roo.bootstrap.DocumentManager} this
28699          * @param {Object} file
28700          */
28701         "click" : true,
28702         /**
28703          * @event edit
28704          * Fire when upload a image and editable set to true
28705          * @param {Roo.bootstrap.DocumentManager} this
28706          * @param {Object} file
28707          */
28708         "edit" : true,
28709         /**
28710          * @event beforeselectfile
28711          * Fire before select file
28712          * @param {Roo.bootstrap.DocumentManager} this
28713          */
28714         "beforeselectfile" : true,
28715         /**
28716          * @event process
28717          * Fire before process file
28718          * @param {Roo.bootstrap.DocumentManager} this
28719          * @param {Object} file
28720          */
28721         "process" : true,
28722         /**
28723          * @event previewrendered
28724          * Fire when preview rendered
28725          * @param {Roo.bootstrap.DocumentManager} this
28726          * @param {Object} file
28727          */
28728         "previewrendered" : true,
28729         /**
28730          */
28731         "previewResize" : true
28732         
28733     });
28734 };
28735
28736 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28737     
28738     boxes : 0,
28739     inputName : '',
28740     thumbSize : 300,
28741     multiple : true,
28742     files : false,
28743     method : 'POST',
28744     url : '',
28745     paramName : 'imageUpload',
28746     toolTipName : 'filename',
28747     fieldLabel : '',
28748     labelWidth : 4,
28749     labelAlign : 'left',
28750     editable : true,
28751     delegates : false,
28752     xhr : false, 
28753     
28754     labellg : 0,
28755     labelmd : 0,
28756     labelsm : 0,
28757     labelxs : 0,
28758     
28759     getAutoCreate : function()
28760     {   
28761         var managerWidget = {
28762             tag : 'div',
28763             cls : 'roo-document-manager',
28764             cn : [
28765                 {
28766                     tag : 'input',
28767                     cls : 'roo-document-manager-selector',
28768                     type : 'file'
28769                 },
28770                 {
28771                     tag : 'div',
28772                     cls : 'roo-document-manager-uploader',
28773                     cn : [
28774                         {
28775                             tag : 'div',
28776                             cls : 'roo-document-manager-upload-btn',
28777                             html : '<i class="fa fa-plus"></i>'
28778                         }
28779                     ]
28780                     
28781                 }
28782             ]
28783         };
28784         
28785         var content = [
28786             {
28787                 tag : 'div',
28788                 cls : 'column col-md-12',
28789                 cn : managerWidget
28790             }
28791         ];
28792         
28793         if(this.fieldLabel.length){
28794             
28795             content = [
28796                 {
28797                     tag : 'div',
28798                     cls : 'column col-md-12',
28799                     html : this.fieldLabel
28800                 },
28801                 {
28802                     tag : 'div',
28803                     cls : 'column col-md-12',
28804                     cn : managerWidget
28805                 }
28806             ];
28807
28808             if(this.labelAlign == 'left'){
28809                 content = [
28810                     {
28811                         tag : 'div',
28812                         cls : 'column',
28813                         html : this.fieldLabel
28814                     },
28815                     {
28816                         tag : 'div',
28817                         cls : 'column',
28818                         cn : managerWidget
28819                     }
28820                 ];
28821                 
28822                 if(this.labelWidth > 12){
28823                     content[0].style = "width: " + this.labelWidth + 'px';
28824                 }
28825
28826                 if(this.labelWidth < 13 && this.labelmd == 0){
28827                     this.labelmd = this.labelWidth;
28828                 }
28829
28830                 if(this.labellg > 0){
28831                     content[0].cls += ' col-lg-' + this.labellg;
28832                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28833                 }
28834
28835                 if(this.labelmd > 0){
28836                     content[0].cls += ' col-md-' + this.labelmd;
28837                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28838                 }
28839
28840                 if(this.labelsm > 0){
28841                     content[0].cls += ' col-sm-' + this.labelsm;
28842                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28843                 }
28844
28845                 if(this.labelxs > 0){
28846                     content[0].cls += ' col-xs-' + this.labelxs;
28847                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28848                 }
28849                 
28850             }
28851         }
28852         
28853         var cfg = {
28854             tag : 'div',
28855             cls : 'row clearfix',
28856             cn : content
28857         };
28858         
28859         return cfg;
28860         
28861     },
28862     
28863     initEvents : function()
28864     {
28865         this.managerEl = this.el.select('.roo-document-manager', true).first();
28866         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28867         
28868         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28869         this.selectorEl.hide();
28870         
28871         if(this.multiple){
28872             this.selectorEl.attr('multiple', 'multiple');
28873         }
28874         
28875         this.selectorEl.on('change', this.onFileSelected, this);
28876         
28877         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28878         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28879         
28880         this.uploader.on('click', this.onUploaderClick, this);
28881         
28882         this.renderProgressDialog();
28883         
28884         var _this = this;
28885         
28886         window.addEventListener("resize", function() { _this.refresh(); } );
28887         
28888         this.fireEvent('initial', this);
28889     },
28890     
28891     renderProgressDialog : function()
28892     {
28893         var _this = this;
28894         
28895         this.progressDialog = new Roo.bootstrap.Modal({
28896             cls : 'roo-document-manager-progress-dialog',
28897             allow_close : false,
28898             title : '',
28899             buttons : [
28900                 {
28901                     name  :'cancel',
28902                     weight : 'danger',
28903                     html : 'Cancel'
28904                 }
28905             ], 
28906             listeners : { 
28907                 btnclick : function() {
28908                     _this.uploadCancel();
28909                     this.hide();
28910                 }
28911             }
28912         });
28913          
28914         this.progressDialog.render(Roo.get(document.body));
28915          
28916         this.progress = new Roo.bootstrap.Progress({
28917             cls : 'roo-document-manager-progress',
28918             active : true,
28919             striped : true
28920         });
28921         
28922         this.progress.render(this.progressDialog.getChildContainer());
28923         
28924         this.progressBar = new Roo.bootstrap.ProgressBar({
28925             cls : 'roo-document-manager-progress-bar',
28926             aria_valuenow : 0,
28927             aria_valuemin : 0,
28928             aria_valuemax : 12,
28929             panel : 'success'
28930         });
28931         
28932         this.progressBar.render(this.progress.getChildContainer());
28933     },
28934     
28935     onUploaderClick : function(e)
28936     {
28937         e.preventDefault();
28938      
28939         if(this.fireEvent('beforeselectfile', this) != false){
28940             this.selectorEl.dom.click();
28941         }
28942         
28943     },
28944     
28945     onFileSelected : function(e)
28946     {
28947         e.preventDefault();
28948         
28949         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28950             return;
28951         }
28952         
28953         Roo.each(this.selectorEl.dom.files, function(file){
28954             if(this.fireEvent('inspect', this, file) != false){
28955                 this.files.push(file);
28956             }
28957         }, this);
28958         
28959         this.queue();
28960         
28961     },
28962     
28963     queue : function()
28964     {
28965         this.selectorEl.dom.value = '';
28966         
28967         if(!this.files || !this.files.length){
28968             return;
28969         }
28970         
28971         if(this.boxes > 0 && this.files.length > this.boxes){
28972             this.files = this.files.slice(0, this.boxes);
28973         }
28974         
28975         this.uploader.show();
28976         
28977         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28978             this.uploader.hide();
28979         }
28980         
28981         var _this = this;
28982         
28983         var files = [];
28984         
28985         var docs = [];
28986         
28987         Roo.each(this.files, function(file){
28988             
28989             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28990                 var f = this.renderPreview(file);
28991                 files.push(f);
28992                 return;
28993             }
28994             
28995             if(file.type.indexOf('image') != -1){
28996                 this.delegates.push(
28997                     (function(){
28998                         _this.process(file);
28999                     }).createDelegate(this)
29000                 );
29001         
29002                 return;
29003             }
29004             
29005             docs.push(
29006                 (function(){
29007                     _this.process(file);
29008                 }).createDelegate(this)
29009             );
29010             
29011         }, this);
29012         
29013         this.files = files;
29014         
29015         this.delegates = this.delegates.concat(docs);
29016         
29017         if(!this.delegates.length){
29018             this.refresh();
29019             return;
29020         }
29021         
29022         this.progressBar.aria_valuemax = this.delegates.length;
29023         
29024         this.arrange();
29025         
29026         return;
29027     },
29028     
29029     arrange : function()
29030     {
29031         if(!this.delegates.length){
29032             this.progressDialog.hide();
29033             this.refresh();
29034             return;
29035         }
29036         
29037         var delegate = this.delegates.shift();
29038         
29039         this.progressDialog.show();
29040         
29041         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29042         
29043         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29044         
29045         delegate();
29046     },
29047     
29048     refresh : function()
29049     {
29050         this.uploader.show();
29051         
29052         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29053             this.uploader.hide();
29054         }
29055         
29056         Roo.isTouch ? this.closable(false) : this.closable(true);
29057         
29058         this.fireEvent('refresh', this);
29059     },
29060     
29061     onRemove : function(e, el, o)
29062     {
29063         e.preventDefault();
29064         
29065         this.fireEvent('remove', this, o);
29066         
29067     },
29068     
29069     remove : function(o)
29070     {
29071         var files = [];
29072         
29073         Roo.each(this.files, function(file){
29074             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29075                 files.push(file);
29076                 return;
29077             }
29078
29079             o.target.remove();
29080
29081         }, this);
29082         
29083         this.files = files;
29084         
29085         this.refresh();
29086     },
29087     
29088     clear : function()
29089     {
29090         Roo.each(this.files, function(file){
29091             if(!file.target){
29092                 return;
29093             }
29094             
29095             file.target.remove();
29096
29097         }, this);
29098         
29099         this.files = [];
29100         
29101         this.refresh();
29102     },
29103     
29104     onClick : function(e, el, o)
29105     {
29106         e.preventDefault();
29107         
29108         this.fireEvent('click', this, o);
29109         
29110     },
29111     
29112     closable : function(closable)
29113     {
29114         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29115             
29116             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29117             
29118             if(closable){
29119                 el.show();
29120                 return;
29121             }
29122             
29123             el.hide();
29124             
29125         }, this);
29126     },
29127     
29128     xhrOnLoad : function(xhr)
29129     {
29130         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29131             el.remove();
29132         }, this);
29133         
29134         if (xhr.readyState !== 4) {
29135             this.arrange();
29136             this.fireEvent('exception', this, xhr);
29137             return;
29138         }
29139
29140         var response = Roo.decode(xhr.responseText);
29141         
29142         if(!response.success){
29143             this.arrange();
29144             this.fireEvent('exception', this, xhr);
29145             return;
29146         }
29147         
29148         var file = this.renderPreview(response.data);
29149         
29150         this.files.push(file);
29151         
29152         this.arrange();
29153         
29154         this.fireEvent('afterupload', this, xhr);
29155         
29156     },
29157     
29158     xhrOnError : function(xhr)
29159     {
29160         Roo.log('xhr on error');
29161         
29162         var response = Roo.decode(xhr.responseText);
29163           
29164         Roo.log(response);
29165         
29166         this.arrange();
29167     },
29168     
29169     process : function(file)
29170     {
29171         if(this.fireEvent('process', this, file) !== false){
29172             if(this.editable && file.type.indexOf('image') != -1){
29173                 this.fireEvent('edit', this, file);
29174                 return;
29175             }
29176
29177             this.uploadStart(file, false);
29178
29179             return;
29180         }
29181         
29182     },
29183     
29184     uploadStart : function(file, crop)
29185     {
29186         this.xhr = new XMLHttpRequest();
29187         
29188         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29189             this.arrange();
29190             return;
29191         }
29192         
29193         file.xhr = this.xhr;
29194             
29195         this.managerEl.createChild({
29196             tag : 'div',
29197             cls : 'roo-document-manager-loading',
29198             cn : [
29199                 {
29200                     tag : 'div',
29201                     tooltip : file.name,
29202                     cls : 'roo-document-manager-thumb',
29203                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29204                 }
29205             ]
29206
29207         });
29208
29209         this.xhr.open(this.method, this.url, true);
29210         
29211         var headers = {
29212             "Accept": "application/json",
29213             "Cache-Control": "no-cache",
29214             "X-Requested-With": "XMLHttpRequest"
29215         };
29216         
29217         for (var headerName in headers) {
29218             var headerValue = headers[headerName];
29219             if (headerValue) {
29220                 this.xhr.setRequestHeader(headerName, headerValue);
29221             }
29222         }
29223         
29224         var _this = this;
29225         
29226         this.xhr.onload = function()
29227         {
29228             _this.xhrOnLoad(_this.xhr);
29229         }
29230         
29231         this.xhr.onerror = function()
29232         {
29233             _this.xhrOnError(_this.xhr);
29234         }
29235         
29236         var formData = new FormData();
29237
29238         formData.append('returnHTML', 'NO');
29239         
29240         if(crop){
29241             formData.append('crop', crop);
29242         }
29243         
29244         formData.append(this.paramName, file, file.name);
29245         
29246         var options = {
29247             file : file, 
29248             manually : false
29249         };
29250         
29251         if(this.fireEvent('prepare', this, formData, options) != false){
29252             
29253             if(options.manually){
29254                 return;
29255             }
29256             
29257             this.xhr.send(formData);
29258             return;
29259         };
29260         
29261         this.uploadCancel();
29262     },
29263     
29264     uploadCancel : function()
29265     {
29266         if (this.xhr) {
29267             this.xhr.abort();
29268         }
29269         
29270         this.delegates = [];
29271         
29272         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29273             el.remove();
29274         }, this);
29275         
29276         this.arrange();
29277     },
29278     
29279     renderPreview : function(file)
29280     {
29281         if(typeof(file.target) != 'undefined' && file.target){
29282             return file;
29283         }
29284         
29285         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29286         
29287         var previewEl = this.managerEl.createChild({
29288             tag : 'div',
29289             cls : 'roo-document-manager-preview',
29290             cn : [
29291                 {
29292                     tag : 'div',
29293                     tooltip : file[this.toolTipName],
29294                     cls : 'roo-document-manager-thumb',
29295                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29296                 },
29297                 {
29298                     tag : 'button',
29299                     cls : 'close',
29300                     html : '<i class="fa fa-times-circle"></i>'
29301                 }
29302             ]
29303         });
29304
29305         var close = previewEl.select('button.close', true).first();
29306
29307         close.on('click', this.onRemove, this, file);
29308
29309         file.target = previewEl;
29310
29311         var image = previewEl.select('img', true).first();
29312         
29313         var _this = this;
29314         
29315         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29316         
29317         image.on('click', this.onClick, this, file);
29318         
29319         this.fireEvent('previewrendered', this, file);
29320         
29321         return file;
29322         
29323     },
29324     
29325     onPreviewLoad : function(file, image)
29326     {
29327         if(typeof(file.target) == 'undefined' || !file.target){
29328             return;
29329         }
29330         
29331         var width = image.dom.naturalWidth || image.dom.width;
29332         var height = image.dom.naturalHeight || image.dom.height;
29333         
29334         if(!this.previewResize) {
29335             return;
29336         }
29337         
29338         if(width > height){
29339             file.target.addClass('wide');
29340             return;
29341         }
29342         
29343         file.target.addClass('tall');
29344         return;
29345         
29346     },
29347     
29348     uploadFromSource : function(file, crop)
29349     {
29350         this.xhr = new XMLHttpRequest();
29351         
29352         this.managerEl.createChild({
29353             tag : 'div',
29354             cls : 'roo-document-manager-loading',
29355             cn : [
29356                 {
29357                     tag : 'div',
29358                     tooltip : file.name,
29359                     cls : 'roo-document-manager-thumb',
29360                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29361                 }
29362             ]
29363
29364         });
29365
29366         this.xhr.open(this.method, this.url, true);
29367         
29368         var headers = {
29369             "Accept": "application/json",
29370             "Cache-Control": "no-cache",
29371             "X-Requested-With": "XMLHttpRequest"
29372         };
29373         
29374         for (var headerName in headers) {
29375             var headerValue = headers[headerName];
29376             if (headerValue) {
29377                 this.xhr.setRequestHeader(headerName, headerValue);
29378             }
29379         }
29380         
29381         var _this = this;
29382         
29383         this.xhr.onload = function()
29384         {
29385             _this.xhrOnLoad(_this.xhr);
29386         }
29387         
29388         this.xhr.onerror = function()
29389         {
29390             _this.xhrOnError(_this.xhr);
29391         }
29392         
29393         var formData = new FormData();
29394
29395         formData.append('returnHTML', 'NO');
29396         
29397         formData.append('crop', crop);
29398         
29399         if(typeof(file.filename) != 'undefined'){
29400             formData.append('filename', file.filename);
29401         }
29402         
29403         if(typeof(file.mimetype) != 'undefined'){
29404             formData.append('mimetype', file.mimetype);
29405         }
29406         
29407         Roo.log(formData);
29408         
29409         if(this.fireEvent('prepare', this, formData) != false){
29410             this.xhr.send(formData);
29411         };
29412     }
29413 });
29414
29415 /*
29416 * Licence: LGPL
29417 */
29418
29419 /**
29420  * @class Roo.bootstrap.DocumentViewer
29421  * @extends Roo.bootstrap.Component
29422  * Bootstrap DocumentViewer class
29423  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29424  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29425  * 
29426  * @constructor
29427  * Create a new DocumentViewer
29428  * @param {Object} config The config object
29429  */
29430
29431 Roo.bootstrap.DocumentViewer = function(config){
29432     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29433     
29434     this.addEvents({
29435         /**
29436          * @event initial
29437          * Fire after initEvent
29438          * @param {Roo.bootstrap.DocumentViewer} this
29439          */
29440         "initial" : true,
29441         /**
29442          * @event click
29443          * Fire after click
29444          * @param {Roo.bootstrap.DocumentViewer} this
29445          */
29446         "click" : true,
29447         /**
29448          * @event download
29449          * Fire after download button
29450          * @param {Roo.bootstrap.DocumentViewer} this
29451          */
29452         "download" : true,
29453         /**
29454          * @event trash
29455          * Fire after trash button
29456          * @param {Roo.bootstrap.DocumentViewer} this
29457          */
29458         "trash" : true
29459         
29460     });
29461 };
29462
29463 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29464     
29465     showDownload : true,
29466     
29467     showTrash : true,
29468     
29469     getAutoCreate : function()
29470     {
29471         var cfg = {
29472             tag : 'div',
29473             cls : 'roo-document-viewer',
29474             cn : [
29475                 {
29476                     tag : 'div',
29477                     cls : 'roo-document-viewer-body',
29478                     cn : [
29479                         {
29480                             tag : 'div',
29481                             cls : 'roo-document-viewer-thumb',
29482                             cn : [
29483                                 {
29484                                     tag : 'img',
29485                                     cls : 'roo-document-viewer-image'
29486                                 }
29487                             ]
29488                         }
29489                     ]
29490                 },
29491                 {
29492                     tag : 'div',
29493                     cls : 'roo-document-viewer-footer',
29494                     cn : {
29495                         tag : 'div',
29496                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29497                         cn : [
29498                             {
29499                                 tag : 'div',
29500                                 cls : 'btn-group roo-document-viewer-download',
29501                                 cn : [
29502                                     {
29503                                         tag : 'button',
29504                                         cls : 'btn btn-default',
29505                                         html : '<i class="fa fa-download"></i>'
29506                                     }
29507                                 ]
29508                             },
29509                             {
29510                                 tag : 'div',
29511                                 cls : 'btn-group roo-document-viewer-trash',
29512                                 cn : [
29513                                     {
29514                                         tag : 'button',
29515                                         cls : 'btn btn-default',
29516                                         html : '<i class="fa fa-trash"></i>'
29517                                     }
29518                                 ]
29519                             }
29520                         ]
29521                     }
29522                 }
29523             ]
29524         };
29525         
29526         return cfg;
29527     },
29528     
29529     initEvents : function()
29530     {
29531         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29532         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29533         
29534         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29535         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29536         
29537         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29538         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29539         
29540         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29541         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29542         
29543         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29544         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29545         
29546         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29547         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29548         
29549         this.bodyEl.on('click', this.onClick, this);
29550         this.downloadBtn.on('click', this.onDownload, this);
29551         this.trashBtn.on('click', this.onTrash, this);
29552         
29553         this.downloadBtn.hide();
29554         this.trashBtn.hide();
29555         
29556         if(this.showDownload){
29557             this.downloadBtn.show();
29558         }
29559         
29560         if(this.showTrash){
29561             this.trashBtn.show();
29562         }
29563         
29564         if(!this.showDownload && !this.showTrash) {
29565             this.footerEl.hide();
29566         }
29567         
29568     },
29569     
29570     initial : function()
29571     {
29572         this.fireEvent('initial', this);
29573         
29574     },
29575     
29576     onClick : function(e)
29577     {
29578         e.preventDefault();
29579         
29580         this.fireEvent('click', this);
29581     },
29582     
29583     onDownload : function(e)
29584     {
29585         e.preventDefault();
29586         
29587         this.fireEvent('download', this);
29588     },
29589     
29590     onTrash : function(e)
29591     {
29592         e.preventDefault();
29593         
29594         this.fireEvent('trash', this);
29595     }
29596     
29597 });
29598 /*
29599  * - LGPL
29600  *
29601  * nav progress bar
29602  * 
29603  */
29604
29605 /**
29606  * @class Roo.bootstrap.NavProgressBar
29607  * @extends Roo.bootstrap.Component
29608  * Bootstrap NavProgressBar class
29609  * 
29610  * @constructor
29611  * Create a new nav progress bar
29612  * @param {Object} config The config object
29613  */
29614
29615 Roo.bootstrap.NavProgressBar = function(config){
29616     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29617
29618     this.bullets = this.bullets || [];
29619    
29620 //    Roo.bootstrap.NavProgressBar.register(this);
29621      this.addEvents({
29622         /**
29623              * @event changed
29624              * Fires when the active item changes
29625              * @param {Roo.bootstrap.NavProgressBar} this
29626              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29627              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29628          */
29629         'changed': true
29630      });
29631     
29632 };
29633
29634 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29635     
29636     bullets : [],
29637     barItems : [],
29638     
29639     getAutoCreate : function()
29640     {
29641         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29642         
29643         cfg = {
29644             tag : 'div',
29645             cls : 'roo-navigation-bar-group',
29646             cn : [
29647                 {
29648                     tag : 'div',
29649                     cls : 'roo-navigation-top-bar'
29650                 },
29651                 {
29652                     tag : 'div',
29653                     cls : 'roo-navigation-bullets-bar',
29654                     cn : [
29655                         {
29656                             tag : 'ul',
29657                             cls : 'roo-navigation-bar'
29658                         }
29659                     ]
29660                 },
29661                 
29662                 {
29663                     tag : 'div',
29664                     cls : 'roo-navigation-bottom-bar'
29665                 }
29666             ]
29667             
29668         };
29669         
29670         return cfg;
29671         
29672     },
29673     
29674     initEvents: function() 
29675     {
29676         
29677     },
29678     
29679     onRender : function(ct, position) 
29680     {
29681         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29682         
29683         if(this.bullets.length){
29684             Roo.each(this.bullets, function(b){
29685                this.addItem(b);
29686             }, this);
29687         }
29688         
29689         this.format();
29690         
29691     },
29692     
29693     addItem : function(cfg)
29694     {
29695         var item = new Roo.bootstrap.NavProgressItem(cfg);
29696         
29697         item.parentId = this.id;
29698         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29699         
29700         if(cfg.html){
29701             var top = new Roo.bootstrap.Element({
29702                 tag : 'div',
29703                 cls : 'roo-navigation-bar-text'
29704             });
29705             
29706             var bottom = new Roo.bootstrap.Element({
29707                 tag : 'div',
29708                 cls : 'roo-navigation-bar-text'
29709             });
29710             
29711             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29712             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29713             
29714             var topText = new Roo.bootstrap.Element({
29715                 tag : 'span',
29716                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29717             });
29718             
29719             var bottomText = new Roo.bootstrap.Element({
29720                 tag : 'span',
29721                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29722             });
29723             
29724             topText.onRender(top.el, null);
29725             bottomText.onRender(bottom.el, null);
29726             
29727             item.topEl = top;
29728             item.bottomEl = bottom;
29729         }
29730         
29731         this.barItems.push(item);
29732         
29733         return item;
29734     },
29735     
29736     getActive : function()
29737     {
29738         var active = false;
29739         
29740         Roo.each(this.barItems, function(v){
29741             
29742             if (!v.isActive()) {
29743                 return;
29744             }
29745             
29746             active = v;
29747             return false;
29748             
29749         });
29750         
29751         return active;
29752     },
29753     
29754     setActiveItem : function(item)
29755     {
29756         var prev = false;
29757         
29758         Roo.each(this.barItems, function(v){
29759             if (v.rid == item.rid) {
29760                 return ;
29761             }
29762             
29763             if (v.isActive()) {
29764                 v.setActive(false);
29765                 prev = v;
29766             }
29767         });
29768
29769         item.setActive(true);
29770         
29771         this.fireEvent('changed', this, item, prev);
29772     },
29773     
29774     getBarItem: function(rid)
29775     {
29776         var ret = false;
29777         
29778         Roo.each(this.barItems, function(e) {
29779             if (e.rid != rid) {
29780                 return;
29781             }
29782             
29783             ret =  e;
29784             return false;
29785         });
29786         
29787         return ret;
29788     },
29789     
29790     indexOfItem : function(item)
29791     {
29792         var index = false;
29793         
29794         Roo.each(this.barItems, function(v, i){
29795             
29796             if (v.rid != item.rid) {
29797                 return;
29798             }
29799             
29800             index = i;
29801             return false
29802         });
29803         
29804         return index;
29805     },
29806     
29807     setActiveNext : function()
29808     {
29809         var i = this.indexOfItem(this.getActive());
29810         
29811         if (i > this.barItems.length) {
29812             return;
29813         }
29814         
29815         this.setActiveItem(this.barItems[i+1]);
29816     },
29817     
29818     setActivePrev : function()
29819     {
29820         var i = this.indexOfItem(this.getActive());
29821         
29822         if (i  < 1) {
29823             return;
29824         }
29825         
29826         this.setActiveItem(this.barItems[i-1]);
29827     },
29828     
29829     format : function()
29830     {
29831         if(!this.barItems.length){
29832             return;
29833         }
29834      
29835         var width = 100 / this.barItems.length;
29836         
29837         Roo.each(this.barItems, function(i){
29838             i.el.setStyle('width', width + '%');
29839             i.topEl.el.setStyle('width', width + '%');
29840             i.bottomEl.el.setStyle('width', width + '%');
29841         }, this);
29842         
29843     }
29844     
29845 });
29846 /*
29847  * - LGPL
29848  *
29849  * Nav Progress Item
29850  * 
29851  */
29852
29853 /**
29854  * @class Roo.bootstrap.NavProgressItem
29855  * @extends Roo.bootstrap.Component
29856  * Bootstrap NavProgressItem class
29857  * @cfg {String} rid the reference id
29858  * @cfg {Boolean} active (true|false) Is item active default false
29859  * @cfg {Boolean} disabled (true|false) Is item active default false
29860  * @cfg {String} html
29861  * @cfg {String} position (top|bottom) text position default bottom
29862  * @cfg {String} icon show icon instead of number
29863  * 
29864  * @constructor
29865  * Create a new NavProgressItem
29866  * @param {Object} config The config object
29867  */
29868 Roo.bootstrap.NavProgressItem = function(config){
29869     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29870     this.addEvents({
29871         // raw events
29872         /**
29873          * @event click
29874          * The raw click event for the entire grid.
29875          * @param {Roo.bootstrap.NavProgressItem} this
29876          * @param {Roo.EventObject} e
29877          */
29878         "click" : true
29879     });
29880    
29881 };
29882
29883 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29884     
29885     rid : '',
29886     active : false,
29887     disabled : false,
29888     html : '',
29889     position : 'bottom',
29890     icon : false,
29891     
29892     getAutoCreate : function()
29893     {
29894         var iconCls = 'roo-navigation-bar-item-icon';
29895         
29896         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29897         
29898         var cfg = {
29899             tag: 'li',
29900             cls: 'roo-navigation-bar-item',
29901             cn : [
29902                 {
29903                     tag : 'i',
29904                     cls : iconCls
29905                 }
29906             ]
29907         };
29908         
29909         if(this.active){
29910             cfg.cls += ' active';
29911         }
29912         if(this.disabled){
29913             cfg.cls += ' disabled';
29914         }
29915         
29916         return cfg;
29917     },
29918     
29919     disable : function()
29920     {
29921         this.setDisabled(true);
29922     },
29923     
29924     enable : function()
29925     {
29926         this.setDisabled(false);
29927     },
29928     
29929     initEvents: function() 
29930     {
29931         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29932         
29933         this.iconEl.on('click', this.onClick, this);
29934     },
29935     
29936     onClick : function(e)
29937     {
29938         e.preventDefault();
29939         
29940         if(this.disabled){
29941             return;
29942         }
29943         
29944         if(this.fireEvent('click', this, e) === false){
29945             return;
29946         };
29947         
29948         this.parent().setActiveItem(this);
29949     },
29950     
29951     isActive: function () 
29952     {
29953         return this.active;
29954     },
29955     
29956     setActive : function(state)
29957     {
29958         if(this.active == state){
29959             return;
29960         }
29961         
29962         this.active = state;
29963         
29964         if (state) {
29965             this.el.addClass('active');
29966             return;
29967         }
29968         
29969         this.el.removeClass('active');
29970         
29971         return;
29972     },
29973     
29974     setDisabled : function(state)
29975     {
29976         if(this.disabled == state){
29977             return;
29978         }
29979         
29980         this.disabled = state;
29981         
29982         if (state) {
29983             this.el.addClass('disabled');
29984             return;
29985         }
29986         
29987         this.el.removeClass('disabled');
29988     },
29989     
29990     tooltipEl : function()
29991     {
29992         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29993     }
29994 });
29995  
29996
29997  /*
29998  * - LGPL
29999  *
30000  * FieldLabel
30001  * 
30002  */
30003
30004 /**
30005  * @class Roo.bootstrap.FieldLabel
30006  * @extends Roo.bootstrap.Component
30007  * Bootstrap FieldLabel class
30008  * @cfg {String} html contents of the element
30009  * @cfg {String} tag tag of the element default label
30010  * @cfg {String} cls class of the element
30011  * @cfg {String} target label target 
30012  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30013  * @cfg {String} invalidClass default "text-warning"
30014  * @cfg {String} validClass default "text-success"
30015  * @cfg {String} iconTooltip default "This field is required"
30016  * @cfg {String} indicatorpos (left|right) default left
30017  * 
30018  * @constructor
30019  * Create a new FieldLabel
30020  * @param {Object} config The config object
30021  */
30022
30023 Roo.bootstrap.FieldLabel = function(config){
30024     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30025     
30026     this.addEvents({
30027             /**
30028              * @event invalid
30029              * Fires after the field has been marked as invalid.
30030              * @param {Roo.form.FieldLabel} this
30031              * @param {String} msg The validation message
30032              */
30033             invalid : true,
30034             /**
30035              * @event valid
30036              * Fires after the field has been validated with no errors.
30037              * @param {Roo.form.FieldLabel} this
30038              */
30039             valid : true
30040         });
30041 };
30042
30043 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30044     
30045     tag: 'label',
30046     cls: '',
30047     html: '',
30048     target: '',
30049     allowBlank : true,
30050     invalidClass : 'has-warning',
30051     validClass : 'has-success',
30052     iconTooltip : 'This field is required',
30053     indicatorpos : 'left',
30054     
30055     getAutoCreate : function(){
30056         
30057         var cls = "";
30058         if (!this.allowBlank) {
30059             cls  = "visible";
30060         }
30061         
30062         var cfg = {
30063             tag : this.tag,
30064             cls : 'roo-bootstrap-field-label ' + this.cls,
30065             for : this.target,
30066             cn : [
30067                 {
30068                     tag : 'i',
30069                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30070                     tooltip : this.iconTooltip
30071                 },
30072                 {
30073                     tag : 'span',
30074                     html : this.html
30075                 }
30076             ] 
30077         };
30078         
30079         if(this.indicatorpos == 'right'){
30080             var cfg = {
30081                 tag : this.tag,
30082                 cls : 'roo-bootstrap-field-label ' + this.cls,
30083                 for : this.target,
30084                 cn : [
30085                     {
30086                         tag : 'span',
30087                         html : this.html
30088                     },
30089                     {
30090                         tag : 'i',
30091                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30092                         tooltip : this.iconTooltip
30093                     }
30094                 ] 
30095             };
30096         }
30097         
30098         return cfg;
30099     },
30100     
30101     initEvents: function() 
30102     {
30103         Roo.bootstrap.Element.superclass.initEvents.call(this);
30104         
30105         this.indicator = this.indicatorEl();
30106         
30107         if(this.indicator){
30108             this.indicator.removeClass('visible');
30109             this.indicator.addClass('invisible');
30110         }
30111         
30112         Roo.bootstrap.FieldLabel.register(this);
30113     },
30114     
30115     indicatorEl : function()
30116     {
30117         var indicator = this.el.select('i.roo-required-indicator',true).first();
30118         
30119         if(!indicator){
30120             return false;
30121         }
30122         
30123         return indicator;
30124         
30125     },
30126     
30127     /**
30128      * Mark this field as valid
30129      */
30130     markValid : function()
30131     {
30132         if(this.indicator){
30133             this.indicator.removeClass('visible');
30134             this.indicator.addClass('invisible');
30135         }
30136         
30137         this.el.removeClass(this.invalidClass);
30138         
30139         this.el.addClass(this.validClass);
30140         
30141         this.fireEvent('valid', this);
30142     },
30143     
30144     /**
30145      * Mark this field as invalid
30146      * @param {String} msg The validation message
30147      */
30148     markInvalid : function(msg)
30149     {
30150         if(this.indicator){
30151             this.indicator.removeClass('invisible');
30152             this.indicator.addClass('visible');
30153         }
30154         
30155         this.el.removeClass(this.validClass);
30156         
30157         this.el.addClass(this.invalidClass);
30158         
30159         this.fireEvent('invalid', this, msg);
30160     }
30161     
30162    
30163 });
30164
30165 Roo.apply(Roo.bootstrap.FieldLabel, {
30166     
30167     groups: {},
30168     
30169      /**
30170     * register a FieldLabel Group
30171     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30172     */
30173     register : function(label)
30174     {
30175         if(this.groups.hasOwnProperty(label.target)){
30176             return;
30177         }
30178      
30179         this.groups[label.target] = label;
30180         
30181     },
30182     /**
30183     * fetch a FieldLabel Group based on the target
30184     * @param {string} target
30185     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30186     */
30187     get: function(target) {
30188         if (typeof(this.groups[target]) == 'undefined') {
30189             return false;
30190         }
30191         
30192         return this.groups[target] ;
30193     }
30194 });
30195
30196  
30197
30198  /*
30199  * - LGPL
30200  *
30201  * page DateSplitField.
30202  * 
30203  */
30204
30205
30206 /**
30207  * @class Roo.bootstrap.DateSplitField
30208  * @extends Roo.bootstrap.Component
30209  * Bootstrap DateSplitField class
30210  * @cfg {string} fieldLabel - the label associated
30211  * @cfg {Number} labelWidth set the width of label (0-12)
30212  * @cfg {String} labelAlign (top|left)
30213  * @cfg {Boolean} dayAllowBlank (true|false) default false
30214  * @cfg {Boolean} monthAllowBlank (true|false) default false
30215  * @cfg {Boolean} yearAllowBlank (true|false) default false
30216  * @cfg {string} dayPlaceholder 
30217  * @cfg {string} monthPlaceholder
30218  * @cfg {string} yearPlaceholder
30219  * @cfg {string} dayFormat default 'd'
30220  * @cfg {string} monthFormat default 'm'
30221  * @cfg {string} yearFormat default 'Y'
30222  * @cfg {Number} labellg set the width of label (1-12)
30223  * @cfg {Number} labelmd set the width of label (1-12)
30224  * @cfg {Number} labelsm set the width of label (1-12)
30225  * @cfg {Number} labelxs set the width of label (1-12)
30226
30227  *     
30228  * @constructor
30229  * Create a new DateSplitField
30230  * @param {Object} config The config object
30231  */
30232
30233 Roo.bootstrap.DateSplitField = function(config){
30234     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30235     
30236     this.addEvents({
30237         // raw events
30238          /**
30239          * @event years
30240          * getting the data of years
30241          * @param {Roo.bootstrap.DateSplitField} this
30242          * @param {Object} years
30243          */
30244         "years" : true,
30245         /**
30246          * @event days
30247          * getting the data of days
30248          * @param {Roo.bootstrap.DateSplitField} this
30249          * @param {Object} days
30250          */
30251         "days" : true,
30252         /**
30253          * @event invalid
30254          * Fires after the field has been marked as invalid.
30255          * @param {Roo.form.Field} this
30256          * @param {String} msg The validation message
30257          */
30258         invalid : true,
30259        /**
30260          * @event valid
30261          * Fires after the field has been validated with no errors.
30262          * @param {Roo.form.Field} this
30263          */
30264         valid : true
30265     });
30266 };
30267
30268 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30269     
30270     fieldLabel : '',
30271     labelAlign : 'top',
30272     labelWidth : 3,
30273     dayAllowBlank : false,
30274     monthAllowBlank : false,
30275     yearAllowBlank : false,
30276     dayPlaceholder : '',
30277     monthPlaceholder : '',
30278     yearPlaceholder : '',
30279     dayFormat : 'd',
30280     monthFormat : 'm',
30281     yearFormat : 'Y',
30282     isFormField : true,
30283     labellg : 0,
30284     labelmd : 0,
30285     labelsm : 0,
30286     labelxs : 0,
30287     
30288     getAutoCreate : function()
30289     {
30290         var cfg = {
30291             tag : 'div',
30292             cls : 'row roo-date-split-field-group',
30293             cn : [
30294                 {
30295                     tag : 'input',
30296                     type : 'hidden',
30297                     cls : 'form-hidden-field roo-date-split-field-group-value',
30298                     name : this.name
30299                 }
30300             ]
30301         };
30302         
30303         var labelCls = 'col-md-12';
30304         var contentCls = 'col-md-4';
30305         
30306         if(this.fieldLabel){
30307             
30308             var label = {
30309                 tag : 'div',
30310                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30311                 cn : [
30312                     {
30313                         tag : 'label',
30314                         html : this.fieldLabel
30315                     }
30316                 ]
30317             };
30318             
30319             if(this.labelAlign == 'left'){
30320             
30321                 if(this.labelWidth > 12){
30322                     label.style = "width: " + this.labelWidth + 'px';
30323                 }
30324
30325                 if(this.labelWidth < 13 && this.labelmd == 0){
30326                     this.labelmd = this.labelWidth;
30327                 }
30328
30329                 if(this.labellg > 0){
30330                     labelCls = ' col-lg-' + this.labellg;
30331                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30332                 }
30333
30334                 if(this.labelmd > 0){
30335                     labelCls = ' col-md-' + this.labelmd;
30336                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30337                 }
30338
30339                 if(this.labelsm > 0){
30340                     labelCls = ' col-sm-' + this.labelsm;
30341                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30342                 }
30343
30344                 if(this.labelxs > 0){
30345                     labelCls = ' col-xs-' + this.labelxs;
30346                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30347                 }
30348             }
30349             
30350             label.cls += ' ' + labelCls;
30351             
30352             cfg.cn.push(label);
30353         }
30354         
30355         Roo.each(['day', 'month', 'year'], function(t){
30356             cfg.cn.push({
30357                 tag : 'div',
30358                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30359             });
30360         }, this);
30361         
30362         return cfg;
30363     },
30364     
30365     inputEl: function ()
30366     {
30367         return this.el.select('.roo-date-split-field-group-value', true).first();
30368     },
30369     
30370     onRender : function(ct, position) 
30371     {
30372         var _this = this;
30373         
30374         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30375         
30376         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30377         
30378         this.dayField = new Roo.bootstrap.ComboBox({
30379             allowBlank : this.dayAllowBlank,
30380             alwaysQuery : true,
30381             displayField : 'value',
30382             editable : false,
30383             fieldLabel : '',
30384             forceSelection : true,
30385             mode : 'local',
30386             placeholder : this.dayPlaceholder,
30387             selectOnFocus : true,
30388             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30389             triggerAction : 'all',
30390             typeAhead : true,
30391             valueField : 'value',
30392             store : new Roo.data.SimpleStore({
30393                 data : (function() {    
30394                     var days = [];
30395                     _this.fireEvent('days', _this, days);
30396                     return days;
30397                 })(),
30398                 fields : [ 'value' ]
30399             }),
30400             listeners : {
30401                 select : function (_self, record, index)
30402                 {
30403                     _this.setValue(_this.getValue());
30404                 }
30405             }
30406         });
30407
30408         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30409         
30410         this.monthField = new Roo.bootstrap.MonthField({
30411             after : '<i class=\"fa fa-calendar\"></i>',
30412             allowBlank : this.monthAllowBlank,
30413             placeholder : this.monthPlaceholder,
30414             readOnly : true,
30415             listeners : {
30416                 render : function (_self)
30417                 {
30418                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30419                         e.preventDefault();
30420                         _self.focus();
30421                     });
30422                 },
30423                 select : function (_self, oldvalue, newvalue)
30424                 {
30425                     _this.setValue(_this.getValue());
30426                 }
30427             }
30428         });
30429         
30430         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30431         
30432         this.yearField = new Roo.bootstrap.ComboBox({
30433             allowBlank : this.yearAllowBlank,
30434             alwaysQuery : true,
30435             displayField : 'value',
30436             editable : false,
30437             fieldLabel : '',
30438             forceSelection : true,
30439             mode : 'local',
30440             placeholder : this.yearPlaceholder,
30441             selectOnFocus : true,
30442             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30443             triggerAction : 'all',
30444             typeAhead : true,
30445             valueField : 'value',
30446             store : new Roo.data.SimpleStore({
30447                 data : (function() {
30448                     var years = [];
30449                     _this.fireEvent('years', _this, years);
30450                     return years;
30451                 })(),
30452                 fields : [ 'value' ]
30453             }),
30454             listeners : {
30455                 select : function (_self, record, index)
30456                 {
30457                     _this.setValue(_this.getValue());
30458                 }
30459             }
30460         });
30461
30462         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30463     },
30464     
30465     setValue : function(v, format)
30466     {
30467         this.inputEl.dom.value = v;
30468         
30469         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30470         
30471         var d = Date.parseDate(v, f);
30472         
30473         if(!d){
30474             this.validate();
30475             return;
30476         }
30477         
30478         this.setDay(d.format(this.dayFormat));
30479         this.setMonth(d.format(this.monthFormat));
30480         this.setYear(d.format(this.yearFormat));
30481         
30482         this.validate();
30483         
30484         return;
30485     },
30486     
30487     setDay : function(v)
30488     {
30489         this.dayField.setValue(v);
30490         this.inputEl.dom.value = this.getValue();
30491         this.validate();
30492         return;
30493     },
30494     
30495     setMonth : function(v)
30496     {
30497         this.monthField.setValue(v, true);
30498         this.inputEl.dom.value = this.getValue();
30499         this.validate();
30500         return;
30501     },
30502     
30503     setYear : function(v)
30504     {
30505         this.yearField.setValue(v);
30506         this.inputEl.dom.value = this.getValue();
30507         this.validate();
30508         return;
30509     },
30510     
30511     getDay : function()
30512     {
30513         return this.dayField.getValue();
30514     },
30515     
30516     getMonth : function()
30517     {
30518         return this.monthField.getValue();
30519     },
30520     
30521     getYear : function()
30522     {
30523         return this.yearField.getValue();
30524     },
30525     
30526     getValue : function()
30527     {
30528         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30529         
30530         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30531         
30532         return date;
30533     },
30534     
30535     reset : function()
30536     {
30537         this.setDay('');
30538         this.setMonth('');
30539         this.setYear('');
30540         this.inputEl.dom.value = '';
30541         this.validate();
30542         return;
30543     },
30544     
30545     validate : function()
30546     {
30547         var d = this.dayField.validate();
30548         var m = this.monthField.validate();
30549         var y = this.yearField.validate();
30550         
30551         var valid = true;
30552         
30553         if(
30554                 (!this.dayAllowBlank && !d) ||
30555                 (!this.monthAllowBlank && !m) ||
30556                 (!this.yearAllowBlank && !y)
30557         ){
30558             valid = false;
30559         }
30560         
30561         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30562             return valid;
30563         }
30564         
30565         if(valid){
30566             this.markValid();
30567             return valid;
30568         }
30569         
30570         this.markInvalid();
30571         
30572         return valid;
30573     },
30574     
30575     markValid : function()
30576     {
30577         
30578         var label = this.el.select('label', true).first();
30579         var icon = this.el.select('i.fa-star', true).first();
30580
30581         if(label && icon){
30582             icon.remove();
30583         }
30584         
30585         this.fireEvent('valid', this);
30586     },
30587     
30588      /**
30589      * Mark this field as invalid
30590      * @param {String} msg The validation message
30591      */
30592     markInvalid : function(msg)
30593     {
30594         
30595         var label = this.el.select('label', true).first();
30596         var icon = this.el.select('i.fa-star', true).first();
30597
30598         if(label && !icon){
30599             this.el.select('.roo-date-split-field-label', true).createChild({
30600                 tag : 'i',
30601                 cls : 'text-danger fa fa-lg fa-star',
30602                 tooltip : 'This field is required',
30603                 style : 'margin-right:5px;'
30604             }, label, true);
30605         }
30606         
30607         this.fireEvent('invalid', this, msg);
30608     },
30609     
30610     clearInvalid : function()
30611     {
30612         var label = this.el.select('label', true).first();
30613         var icon = this.el.select('i.fa-star', true).first();
30614
30615         if(label && icon){
30616             icon.remove();
30617         }
30618         
30619         this.fireEvent('valid', this);
30620     },
30621     
30622     getName: function()
30623     {
30624         return this.name;
30625     }
30626     
30627 });
30628
30629  /**
30630  *
30631  * This is based on 
30632  * http://masonry.desandro.com
30633  *
30634  * The idea is to render all the bricks based on vertical width...
30635  *
30636  * The original code extends 'outlayer' - we might need to use that....
30637  * 
30638  */
30639
30640
30641 /**
30642  * @class Roo.bootstrap.LayoutMasonry
30643  * @extends Roo.bootstrap.Component
30644  * Bootstrap Layout Masonry class
30645  * 
30646  * @constructor
30647  * Create a new Element
30648  * @param {Object} config The config object
30649  */
30650
30651 Roo.bootstrap.LayoutMasonry = function(config){
30652     
30653     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30654     
30655     this.bricks = [];
30656     
30657     Roo.bootstrap.LayoutMasonry.register(this);
30658     
30659     this.addEvents({
30660         // raw events
30661         /**
30662          * @event layout
30663          * Fire after layout the items
30664          * @param {Roo.bootstrap.LayoutMasonry} this
30665          * @param {Roo.EventObject} e
30666          */
30667         "layout" : true
30668     });
30669     
30670 };
30671
30672 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30673     
30674     /**
30675      * @cfg {Boolean} isLayoutInstant = no animation?
30676      */   
30677     isLayoutInstant : false, // needed?
30678    
30679     /**
30680      * @cfg {Number} boxWidth  width of the columns
30681      */   
30682     boxWidth : 450,
30683     
30684       /**
30685      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30686      */   
30687     boxHeight : 0,
30688     
30689     /**
30690      * @cfg {Number} padWidth padding below box..
30691      */   
30692     padWidth : 10, 
30693     
30694     /**
30695      * @cfg {Number} gutter gutter width..
30696      */   
30697     gutter : 10,
30698     
30699      /**
30700      * @cfg {Number} maxCols maximum number of columns
30701      */   
30702     
30703     maxCols: 0,
30704     
30705     /**
30706      * @cfg {Boolean} isAutoInitial defalut true
30707      */   
30708     isAutoInitial : true, 
30709     
30710     containerWidth: 0,
30711     
30712     /**
30713      * @cfg {Boolean} isHorizontal defalut false
30714      */   
30715     isHorizontal : false, 
30716
30717     currentSize : null,
30718     
30719     tag: 'div',
30720     
30721     cls: '',
30722     
30723     bricks: null, //CompositeElement
30724     
30725     cols : 1,
30726     
30727     _isLayoutInited : false,
30728     
30729 //    isAlternative : false, // only use for vertical layout...
30730     
30731     /**
30732      * @cfg {Number} alternativePadWidth padding below box..
30733      */   
30734     alternativePadWidth : 50,
30735     
30736     selectedBrick : [],
30737     
30738     getAutoCreate : function(){
30739         
30740         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30741         
30742         var cfg = {
30743             tag: this.tag,
30744             cls: 'blog-masonary-wrapper ' + this.cls,
30745             cn : {
30746                 cls : 'mas-boxes masonary'
30747             }
30748         };
30749         
30750         return cfg;
30751     },
30752     
30753     getChildContainer: function( )
30754     {
30755         if (this.boxesEl) {
30756             return this.boxesEl;
30757         }
30758         
30759         this.boxesEl = this.el.select('.mas-boxes').first();
30760         
30761         return this.boxesEl;
30762     },
30763     
30764     
30765     initEvents : function()
30766     {
30767         var _this = this;
30768         
30769         if(this.isAutoInitial){
30770             Roo.log('hook children rendered');
30771             this.on('childrenrendered', function() {
30772                 Roo.log('children rendered');
30773                 _this.initial();
30774             } ,this);
30775         }
30776     },
30777     
30778     initial : function()
30779     {
30780         this.selectedBrick = [];
30781         
30782         this.currentSize = this.el.getBox(true);
30783         
30784         Roo.EventManager.onWindowResize(this.resize, this); 
30785
30786         if(!this.isAutoInitial){
30787             this.layout();
30788             return;
30789         }
30790         
30791         this.layout();
30792         
30793         return;
30794         //this.layout.defer(500,this);
30795         
30796     },
30797     
30798     resize : function()
30799     {
30800         var cs = this.el.getBox(true);
30801         
30802         if (
30803                 this.currentSize.width == cs.width && 
30804                 this.currentSize.x == cs.x && 
30805                 this.currentSize.height == cs.height && 
30806                 this.currentSize.y == cs.y 
30807         ) {
30808             Roo.log("no change in with or X or Y");
30809             return;
30810         }
30811         
30812         this.currentSize = cs;
30813         
30814         this.layout();
30815         
30816     },
30817     
30818     layout : function()
30819     {   
30820         this._resetLayout();
30821         
30822         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30823         
30824         this.layoutItems( isInstant );
30825       
30826         this._isLayoutInited = true;
30827         
30828         this.fireEvent('layout', this);
30829         
30830     },
30831     
30832     _resetLayout : function()
30833     {
30834         if(this.isHorizontal){
30835             this.horizontalMeasureColumns();
30836             return;
30837         }
30838         
30839         this.verticalMeasureColumns();
30840         
30841     },
30842     
30843     verticalMeasureColumns : function()
30844     {
30845         this.getContainerWidth();
30846         
30847 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30848 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30849 //            return;
30850 //        }
30851         
30852         var boxWidth = this.boxWidth + this.padWidth;
30853         
30854         if(this.containerWidth < this.boxWidth){
30855             boxWidth = this.containerWidth
30856         }
30857         
30858         var containerWidth = this.containerWidth;
30859         
30860         var cols = Math.floor(containerWidth / boxWidth);
30861         
30862         this.cols = Math.max( cols, 1 );
30863         
30864         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30865         
30866         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30867         
30868         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30869         
30870         this.colWidth = boxWidth + avail - this.padWidth;
30871         
30872         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30873         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30874     },
30875     
30876     horizontalMeasureColumns : function()
30877     {
30878         this.getContainerWidth();
30879         
30880         var boxWidth = this.boxWidth;
30881         
30882         if(this.containerWidth < boxWidth){
30883             boxWidth = this.containerWidth;
30884         }
30885         
30886         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30887         
30888         this.el.setHeight(boxWidth);
30889         
30890     },
30891     
30892     getContainerWidth : function()
30893     {
30894         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30895     },
30896     
30897     layoutItems : function( isInstant )
30898     {
30899         Roo.log(this.bricks);
30900         
30901         var items = Roo.apply([], this.bricks);
30902         
30903         if(this.isHorizontal){
30904             this._horizontalLayoutItems( items , isInstant );
30905             return;
30906         }
30907         
30908 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30909 //            this._verticalAlternativeLayoutItems( items , isInstant );
30910 //            return;
30911 //        }
30912         
30913         this._verticalLayoutItems( items , isInstant );
30914         
30915     },
30916     
30917     _verticalLayoutItems : function ( items , isInstant)
30918     {
30919         if ( !items || !items.length ) {
30920             return;
30921         }
30922         
30923         var standard = [
30924             ['xs', 'xs', 'xs', 'tall'],
30925             ['xs', 'xs', 'tall'],
30926             ['xs', 'xs', 'sm'],
30927             ['xs', 'xs', 'xs'],
30928             ['xs', 'tall'],
30929             ['xs', 'sm'],
30930             ['xs', 'xs'],
30931             ['xs'],
30932             
30933             ['sm', 'xs', 'xs'],
30934             ['sm', 'xs'],
30935             ['sm'],
30936             
30937             ['tall', 'xs', 'xs', 'xs'],
30938             ['tall', 'xs', 'xs'],
30939             ['tall', 'xs'],
30940             ['tall']
30941             
30942         ];
30943         
30944         var queue = [];
30945         
30946         var boxes = [];
30947         
30948         var box = [];
30949         
30950         Roo.each(items, function(item, k){
30951             
30952             switch (item.size) {
30953                 // these layouts take up a full box,
30954                 case 'md' :
30955                 case 'md-left' :
30956                 case 'md-right' :
30957                 case 'wide' :
30958                     
30959                     if(box.length){
30960                         boxes.push(box);
30961                         box = [];
30962                     }
30963                     
30964                     boxes.push([item]);
30965                     
30966                     break;
30967                     
30968                 case 'xs' :
30969                 case 'sm' :
30970                 case 'tall' :
30971                     
30972                     box.push(item);
30973                     
30974                     break;
30975                 default :
30976                     break;
30977                     
30978             }
30979             
30980         }, this);
30981         
30982         if(box.length){
30983             boxes.push(box);
30984             box = [];
30985         }
30986         
30987         var filterPattern = function(box, length)
30988         {
30989             if(!box.length){
30990                 return;
30991             }
30992             
30993             var match = false;
30994             
30995             var pattern = box.slice(0, length);
30996             
30997             var format = [];
30998             
30999             Roo.each(pattern, function(i){
31000                 format.push(i.size);
31001             }, this);
31002             
31003             Roo.each(standard, function(s){
31004                 
31005                 if(String(s) != String(format)){
31006                     return;
31007                 }
31008                 
31009                 match = true;
31010                 return false;
31011                 
31012             }, this);
31013             
31014             if(!match && length == 1){
31015                 return;
31016             }
31017             
31018             if(!match){
31019                 filterPattern(box, length - 1);
31020                 return;
31021             }
31022                 
31023             queue.push(pattern);
31024
31025             box = box.slice(length, box.length);
31026
31027             filterPattern(box, 4);
31028
31029             return;
31030             
31031         }
31032         
31033         Roo.each(boxes, function(box, k){
31034             
31035             if(!box.length){
31036                 return;
31037             }
31038             
31039             if(box.length == 1){
31040                 queue.push(box);
31041                 return;
31042             }
31043             
31044             filterPattern(box, 4);
31045             
31046         }, this);
31047         
31048         this._processVerticalLayoutQueue( queue, isInstant );
31049         
31050     },
31051     
31052 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31053 //    {
31054 //        if ( !items || !items.length ) {
31055 //            return;
31056 //        }
31057 //
31058 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31059 //        
31060 //    },
31061     
31062     _horizontalLayoutItems : function ( items , isInstant)
31063     {
31064         if ( !items || !items.length || items.length < 3) {
31065             return;
31066         }
31067         
31068         items.reverse();
31069         
31070         var eItems = items.slice(0, 3);
31071         
31072         items = items.slice(3, items.length);
31073         
31074         var standard = [
31075             ['xs', 'xs', 'xs', 'wide'],
31076             ['xs', 'xs', 'wide'],
31077             ['xs', 'xs', 'sm'],
31078             ['xs', 'xs', 'xs'],
31079             ['xs', 'wide'],
31080             ['xs', 'sm'],
31081             ['xs', 'xs'],
31082             ['xs'],
31083             
31084             ['sm', 'xs', 'xs'],
31085             ['sm', 'xs'],
31086             ['sm'],
31087             
31088             ['wide', 'xs', 'xs', 'xs'],
31089             ['wide', 'xs', 'xs'],
31090             ['wide', 'xs'],
31091             ['wide'],
31092             
31093             ['wide-thin']
31094         ];
31095         
31096         var queue = [];
31097         
31098         var boxes = [];
31099         
31100         var box = [];
31101         
31102         Roo.each(items, function(item, k){
31103             
31104             switch (item.size) {
31105                 case 'md' :
31106                 case 'md-left' :
31107                 case 'md-right' :
31108                 case 'tall' :
31109                     
31110                     if(box.length){
31111                         boxes.push(box);
31112                         box = [];
31113                     }
31114                     
31115                     boxes.push([item]);
31116                     
31117                     break;
31118                     
31119                 case 'xs' :
31120                 case 'sm' :
31121                 case 'wide' :
31122                 case 'wide-thin' :
31123                     
31124                     box.push(item);
31125                     
31126                     break;
31127                 default :
31128                     break;
31129                     
31130             }
31131             
31132         }, this);
31133         
31134         if(box.length){
31135             boxes.push(box);
31136             box = [];
31137         }
31138         
31139         var filterPattern = function(box, length)
31140         {
31141             if(!box.length){
31142                 return;
31143             }
31144             
31145             var match = false;
31146             
31147             var pattern = box.slice(0, length);
31148             
31149             var format = [];
31150             
31151             Roo.each(pattern, function(i){
31152                 format.push(i.size);
31153             }, this);
31154             
31155             Roo.each(standard, function(s){
31156                 
31157                 if(String(s) != String(format)){
31158                     return;
31159                 }
31160                 
31161                 match = true;
31162                 return false;
31163                 
31164             }, this);
31165             
31166             if(!match && length == 1){
31167                 return;
31168             }
31169             
31170             if(!match){
31171                 filterPattern(box, length - 1);
31172                 return;
31173             }
31174                 
31175             queue.push(pattern);
31176
31177             box = box.slice(length, box.length);
31178
31179             filterPattern(box, 4);
31180
31181             return;
31182             
31183         }
31184         
31185         Roo.each(boxes, function(box, k){
31186             
31187             if(!box.length){
31188                 return;
31189             }
31190             
31191             if(box.length == 1){
31192                 queue.push(box);
31193                 return;
31194             }
31195             
31196             filterPattern(box, 4);
31197             
31198         }, this);
31199         
31200         
31201         var prune = [];
31202         
31203         var pos = this.el.getBox(true);
31204         
31205         var minX = pos.x;
31206         
31207         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31208         
31209         var hit_end = false;
31210         
31211         Roo.each(queue, function(box){
31212             
31213             if(hit_end){
31214                 
31215                 Roo.each(box, function(b){
31216                 
31217                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31218                     b.el.hide();
31219
31220                 }, this);
31221
31222                 return;
31223             }
31224             
31225             var mx = 0;
31226             
31227             Roo.each(box, function(b){
31228                 
31229                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31230                 b.el.show();
31231
31232                 mx = Math.max(mx, b.x);
31233                 
31234             }, this);
31235             
31236             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31237             
31238             if(maxX < minX){
31239                 
31240                 Roo.each(box, function(b){
31241                 
31242                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31243                     b.el.hide();
31244                     
31245                 }, this);
31246                 
31247                 hit_end = true;
31248                 
31249                 return;
31250             }
31251             
31252             prune.push(box);
31253             
31254         }, this);
31255         
31256         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31257     },
31258     
31259     /** Sets position of item in DOM
31260     * @param {Element} item
31261     * @param {Number} x - horizontal position
31262     * @param {Number} y - vertical position
31263     * @param {Boolean} isInstant - disables transitions
31264     */
31265     _processVerticalLayoutQueue : function( queue, isInstant )
31266     {
31267         var pos = this.el.getBox(true);
31268         var x = pos.x;
31269         var y = pos.y;
31270         var maxY = [];
31271         
31272         for (var i = 0; i < this.cols; i++){
31273             maxY[i] = pos.y;
31274         }
31275         
31276         Roo.each(queue, function(box, k){
31277             
31278             var col = k % this.cols;
31279             
31280             Roo.each(box, function(b,kk){
31281                 
31282                 b.el.position('absolute');
31283                 
31284                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31285                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31286                 
31287                 if(b.size == 'md-left' || b.size == 'md-right'){
31288                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31289                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31290                 }
31291                 
31292                 b.el.setWidth(width);
31293                 b.el.setHeight(height);
31294                 // iframe?
31295                 b.el.select('iframe',true).setSize(width,height);
31296                 
31297             }, this);
31298             
31299             for (var i = 0; i < this.cols; i++){
31300                 
31301                 if(maxY[i] < maxY[col]){
31302                     col = i;
31303                     continue;
31304                 }
31305                 
31306                 col = Math.min(col, i);
31307                 
31308             }
31309             
31310             x = pos.x + col * (this.colWidth + this.padWidth);
31311             
31312             y = maxY[col];
31313             
31314             var positions = [];
31315             
31316             switch (box.length){
31317                 case 1 :
31318                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31319                     break;
31320                 case 2 :
31321                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31322                     break;
31323                 case 3 :
31324                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31325                     break;
31326                 case 4 :
31327                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31328                     break;
31329                 default :
31330                     break;
31331             }
31332             
31333             Roo.each(box, function(b,kk){
31334                 
31335                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31336                 
31337                 var sz = b.el.getSize();
31338                 
31339                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31340                 
31341             }, this);
31342             
31343         }, this);
31344         
31345         var mY = 0;
31346         
31347         for (var i = 0; i < this.cols; i++){
31348             mY = Math.max(mY, maxY[i]);
31349         }
31350         
31351         this.el.setHeight(mY - pos.y);
31352         
31353     },
31354     
31355 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31356 //    {
31357 //        var pos = this.el.getBox(true);
31358 //        var x = pos.x;
31359 //        var y = pos.y;
31360 //        var maxX = pos.right;
31361 //        
31362 //        var maxHeight = 0;
31363 //        
31364 //        Roo.each(items, function(item, k){
31365 //            
31366 //            var c = k % 2;
31367 //            
31368 //            item.el.position('absolute');
31369 //                
31370 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31371 //
31372 //            item.el.setWidth(width);
31373 //
31374 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31375 //
31376 //            item.el.setHeight(height);
31377 //            
31378 //            if(c == 0){
31379 //                item.el.setXY([x, y], isInstant ? false : true);
31380 //            } else {
31381 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31382 //            }
31383 //            
31384 //            y = y + height + this.alternativePadWidth;
31385 //            
31386 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31387 //            
31388 //        }, this);
31389 //        
31390 //        this.el.setHeight(maxHeight);
31391 //        
31392 //    },
31393     
31394     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31395     {
31396         var pos = this.el.getBox(true);
31397         
31398         var minX = pos.x;
31399         var minY = pos.y;
31400         
31401         var maxX = pos.right;
31402         
31403         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31404         
31405         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31406         
31407         Roo.each(queue, function(box, k){
31408             
31409             Roo.each(box, function(b, kk){
31410                 
31411                 b.el.position('absolute');
31412                 
31413                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31414                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31415                 
31416                 if(b.size == 'md-left' || b.size == 'md-right'){
31417                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31418                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31419                 }
31420                 
31421                 b.el.setWidth(width);
31422                 b.el.setHeight(height);
31423                 
31424             }, this);
31425             
31426             if(!box.length){
31427                 return;
31428             }
31429             
31430             var positions = [];
31431             
31432             switch (box.length){
31433                 case 1 :
31434                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31435                     break;
31436                 case 2 :
31437                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31438                     break;
31439                 case 3 :
31440                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31441                     break;
31442                 case 4 :
31443                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31444                     break;
31445                 default :
31446                     break;
31447             }
31448             
31449             Roo.each(box, function(b,kk){
31450                 
31451                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31452                 
31453                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31454                 
31455             }, this);
31456             
31457         }, this);
31458         
31459     },
31460     
31461     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31462     {
31463         Roo.each(eItems, function(b,k){
31464             
31465             b.size = (k == 0) ? 'sm' : 'xs';
31466             b.x = (k == 0) ? 2 : 1;
31467             b.y = (k == 0) ? 2 : 1;
31468             
31469             b.el.position('absolute');
31470             
31471             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31472                 
31473             b.el.setWidth(width);
31474             
31475             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31476             
31477             b.el.setHeight(height);
31478             
31479         }, this);
31480
31481         var positions = [];
31482         
31483         positions.push({
31484             x : maxX - this.unitWidth * 2 - this.gutter,
31485             y : minY
31486         });
31487         
31488         positions.push({
31489             x : maxX - this.unitWidth,
31490             y : minY + (this.unitWidth + this.gutter) * 2
31491         });
31492         
31493         positions.push({
31494             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31495             y : minY
31496         });
31497         
31498         Roo.each(eItems, function(b,k){
31499             
31500             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31501
31502         }, this);
31503         
31504     },
31505     
31506     getVerticalOneBoxColPositions : function(x, y, box)
31507     {
31508         var pos = [];
31509         
31510         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31511         
31512         if(box[0].size == 'md-left'){
31513             rand = 0;
31514         }
31515         
31516         if(box[0].size == 'md-right'){
31517             rand = 1;
31518         }
31519         
31520         pos.push({
31521             x : x + (this.unitWidth + this.gutter) * rand,
31522             y : y
31523         });
31524         
31525         return pos;
31526     },
31527     
31528     getVerticalTwoBoxColPositions : function(x, y, box)
31529     {
31530         var pos = [];
31531         
31532         if(box[0].size == 'xs'){
31533             
31534             pos.push({
31535                 x : x,
31536                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31537             });
31538
31539             pos.push({
31540                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31541                 y : y
31542             });
31543             
31544             return pos;
31545             
31546         }
31547         
31548         pos.push({
31549             x : x,
31550             y : y
31551         });
31552
31553         pos.push({
31554             x : x + (this.unitWidth + this.gutter) * 2,
31555             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31556         });
31557         
31558         return pos;
31559         
31560     },
31561     
31562     getVerticalThreeBoxColPositions : function(x, y, box)
31563     {
31564         var pos = [];
31565         
31566         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31567             
31568             pos.push({
31569                 x : x,
31570                 y : y
31571             });
31572
31573             pos.push({
31574                 x : x + (this.unitWidth + this.gutter) * 1,
31575                 y : y
31576             });
31577             
31578             pos.push({
31579                 x : x + (this.unitWidth + this.gutter) * 2,
31580                 y : y
31581             });
31582             
31583             return pos;
31584             
31585         }
31586         
31587         if(box[0].size == 'xs' && box[1].size == 'xs'){
31588             
31589             pos.push({
31590                 x : x,
31591                 y : y
31592             });
31593
31594             pos.push({
31595                 x : x,
31596                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31597             });
31598             
31599             pos.push({
31600                 x : x + (this.unitWidth + this.gutter) * 1,
31601                 y : y
31602             });
31603             
31604             return pos;
31605             
31606         }
31607         
31608         pos.push({
31609             x : x,
31610             y : y
31611         });
31612
31613         pos.push({
31614             x : x + (this.unitWidth + this.gutter) * 2,
31615             y : y
31616         });
31617
31618         pos.push({
31619             x : x + (this.unitWidth + this.gutter) * 2,
31620             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31621         });
31622             
31623         return pos;
31624         
31625     },
31626     
31627     getVerticalFourBoxColPositions : function(x, y, box)
31628     {
31629         var pos = [];
31630         
31631         if(box[0].size == 'xs'){
31632             
31633             pos.push({
31634                 x : x,
31635                 y : y
31636             });
31637
31638             pos.push({
31639                 x : x,
31640                 y : y + (this.unitHeight + this.gutter) * 1
31641             });
31642             
31643             pos.push({
31644                 x : x,
31645                 y : y + (this.unitHeight + this.gutter) * 2
31646             });
31647             
31648             pos.push({
31649                 x : x + (this.unitWidth + this.gutter) * 1,
31650                 y : y
31651             });
31652             
31653             return pos;
31654             
31655         }
31656         
31657         pos.push({
31658             x : x,
31659             y : y
31660         });
31661
31662         pos.push({
31663             x : x + (this.unitWidth + this.gutter) * 2,
31664             y : y
31665         });
31666
31667         pos.push({
31668             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31669             y : y + (this.unitHeight + this.gutter) * 1
31670         });
31671
31672         pos.push({
31673             x : x + (this.unitWidth + this.gutter) * 2,
31674             y : y + (this.unitWidth + this.gutter) * 2
31675         });
31676
31677         return pos;
31678         
31679     },
31680     
31681     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31682     {
31683         var pos = [];
31684         
31685         if(box[0].size == 'md-left'){
31686             pos.push({
31687                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31688                 y : minY
31689             });
31690             
31691             return pos;
31692         }
31693         
31694         if(box[0].size == 'md-right'){
31695             pos.push({
31696                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31697                 y : minY + (this.unitWidth + this.gutter) * 1
31698             });
31699             
31700             return pos;
31701         }
31702         
31703         var rand = Math.floor(Math.random() * (4 - box[0].y));
31704         
31705         pos.push({
31706             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31707             y : minY + (this.unitWidth + this.gutter) * rand
31708         });
31709         
31710         return pos;
31711         
31712     },
31713     
31714     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31715     {
31716         var pos = [];
31717         
31718         if(box[0].size == 'xs'){
31719             
31720             pos.push({
31721                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31722                 y : minY
31723             });
31724
31725             pos.push({
31726                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31727                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31728             });
31729             
31730             return pos;
31731             
31732         }
31733         
31734         pos.push({
31735             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31736             y : minY
31737         });
31738
31739         pos.push({
31740             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31741             y : minY + (this.unitWidth + this.gutter) * 2
31742         });
31743         
31744         return pos;
31745         
31746     },
31747     
31748     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31749     {
31750         var pos = [];
31751         
31752         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31753             
31754             pos.push({
31755                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31756                 y : minY
31757             });
31758
31759             pos.push({
31760                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31761                 y : minY + (this.unitWidth + this.gutter) * 1
31762             });
31763             
31764             pos.push({
31765                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31766                 y : minY + (this.unitWidth + this.gutter) * 2
31767             });
31768             
31769             return pos;
31770             
31771         }
31772         
31773         if(box[0].size == 'xs' && box[1].size == 'xs'){
31774             
31775             pos.push({
31776                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31777                 y : minY
31778             });
31779
31780             pos.push({
31781                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31782                 y : minY
31783             });
31784             
31785             pos.push({
31786                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31787                 y : minY + (this.unitWidth + this.gutter) * 1
31788             });
31789             
31790             return pos;
31791             
31792         }
31793         
31794         pos.push({
31795             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31796             y : minY
31797         });
31798
31799         pos.push({
31800             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31801             y : minY + (this.unitWidth + this.gutter) * 2
31802         });
31803
31804         pos.push({
31805             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31806             y : minY + (this.unitWidth + this.gutter) * 2
31807         });
31808             
31809         return pos;
31810         
31811     },
31812     
31813     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31814     {
31815         var pos = [];
31816         
31817         if(box[0].size == 'xs'){
31818             
31819             pos.push({
31820                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31821                 y : minY
31822             });
31823
31824             pos.push({
31825                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31826                 y : minY
31827             });
31828             
31829             pos.push({
31830                 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),
31831                 y : minY
31832             });
31833             
31834             pos.push({
31835                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31836                 y : minY + (this.unitWidth + this.gutter) * 1
31837             });
31838             
31839             return pos;
31840             
31841         }
31842         
31843         pos.push({
31844             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31845             y : minY
31846         });
31847         
31848         pos.push({
31849             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31850             y : minY + (this.unitWidth + this.gutter) * 2
31851         });
31852         
31853         pos.push({
31854             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31855             y : minY + (this.unitWidth + this.gutter) * 2
31856         });
31857         
31858         pos.push({
31859             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),
31860             y : minY + (this.unitWidth + this.gutter) * 2
31861         });
31862
31863         return pos;
31864         
31865     },
31866     
31867     /**
31868     * remove a Masonry Brick
31869     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31870     */
31871     removeBrick : function(brick_id)
31872     {
31873         if (!brick_id) {
31874             return;
31875         }
31876         
31877         for (var i = 0; i<this.bricks.length; i++) {
31878             if (this.bricks[i].id == brick_id) {
31879                 this.bricks.splice(i,1);
31880                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31881                 this.initial();
31882             }
31883         }
31884     },
31885     
31886     /**
31887     * adds a Masonry Brick
31888     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31889     */
31890     addBrick : function(cfg)
31891     {
31892         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31893         //this.register(cn);
31894         cn.parentId = this.id;
31895         cn.onRender(this.el, null);
31896         return cn;
31897     },
31898     
31899     /**
31900     * register a Masonry Brick
31901     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31902     */
31903     
31904     register : function(brick)
31905     {
31906         this.bricks.push(brick);
31907         brick.masonryId = this.id;
31908     },
31909     
31910     /**
31911     * clear all the Masonry Brick
31912     */
31913     clearAll : function()
31914     {
31915         this.bricks = [];
31916         //this.getChildContainer().dom.innerHTML = "";
31917         this.el.dom.innerHTML = '';
31918     },
31919     
31920     getSelected : function()
31921     {
31922         if (!this.selectedBrick) {
31923             return false;
31924         }
31925         
31926         return this.selectedBrick;
31927     }
31928 });
31929
31930 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31931     
31932     groups: {},
31933      /**
31934     * register a Masonry Layout
31935     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31936     */
31937     
31938     register : function(layout)
31939     {
31940         this.groups[layout.id] = layout;
31941     },
31942     /**
31943     * fetch a  Masonry Layout based on the masonry layout ID
31944     * @param {string} the masonry layout to add
31945     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31946     */
31947     
31948     get: function(layout_id) {
31949         if (typeof(this.groups[layout_id]) == 'undefined') {
31950             return false;
31951         }
31952         return this.groups[layout_id] ;
31953     }
31954     
31955     
31956     
31957 });
31958
31959  
31960
31961  /**
31962  *
31963  * This is based on 
31964  * http://masonry.desandro.com
31965  *
31966  * The idea is to render all the bricks based on vertical width...
31967  *
31968  * The original code extends 'outlayer' - we might need to use that....
31969  * 
31970  */
31971
31972
31973 /**
31974  * @class Roo.bootstrap.LayoutMasonryAuto
31975  * @extends Roo.bootstrap.Component
31976  * Bootstrap Layout Masonry class
31977  * 
31978  * @constructor
31979  * Create a new Element
31980  * @param {Object} config The config object
31981  */
31982
31983 Roo.bootstrap.LayoutMasonryAuto = function(config){
31984     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31985 };
31986
31987 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31988     
31989       /**
31990      * @cfg {Boolean} isFitWidth  - resize the width..
31991      */   
31992     isFitWidth : false,  // options..
31993     /**
31994      * @cfg {Boolean} isOriginLeft = left align?
31995      */   
31996     isOriginLeft : true,
31997     /**
31998      * @cfg {Boolean} isOriginTop = top align?
31999      */   
32000     isOriginTop : false,
32001     /**
32002      * @cfg {Boolean} isLayoutInstant = no animation?
32003      */   
32004     isLayoutInstant : false, // needed?
32005     /**
32006      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32007      */   
32008     isResizingContainer : true,
32009     /**
32010      * @cfg {Number} columnWidth  width of the columns 
32011      */   
32012     
32013     columnWidth : 0,
32014     
32015     /**
32016      * @cfg {Number} maxCols maximum number of columns
32017      */   
32018     
32019     maxCols: 0,
32020     /**
32021      * @cfg {Number} padHeight padding below box..
32022      */   
32023     
32024     padHeight : 10, 
32025     
32026     /**
32027      * @cfg {Boolean} isAutoInitial defalut true
32028      */   
32029     
32030     isAutoInitial : true, 
32031     
32032     // private?
32033     gutter : 0,
32034     
32035     containerWidth: 0,
32036     initialColumnWidth : 0,
32037     currentSize : null,
32038     
32039     colYs : null, // array.
32040     maxY : 0,
32041     padWidth: 10,
32042     
32043     
32044     tag: 'div',
32045     cls: '',
32046     bricks: null, //CompositeElement
32047     cols : 0, // array?
32048     // element : null, // wrapped now this.el
32049     _isLayoutInited : null, 
32050     
32051     
32052     getAutoCreate : function(){
32053         
32054         var cfg = {
32055             tag: this.tag,
32056             cls: 'blog-masonary-wrapper ' + this.cls,
32057             cn : {
32058                 cls : 'mas-boxes masonary'
32059             }
32060         };
32061         
32062         return cfg;
32063     },
32064     
32065     getChildContainer: function( )
32066     {
32067         if (this.boxesEl) {
32068             return this.boxesEl;
32069         }
32070         
32071         this.boxesEl = this.el.select('.mas-boxes').first();
32072         
32073         return this.boxesEl;
32074     },
32075     
32076     
32077     initEvents : function()
32078     {
32079         var _this = this;
32080         
32081         if(this.isAutoInitial){
32082             Roo.log('hook children rendered');
32083             this.on('childrenrendered', function() {
32084                 Roo.log('children rendered');
32085                 _this.initial();
32086             } ,this);
32087         }
32088         
32089     },
32090     
32091     initial : function()
32092     {
32093         this.reloadItems();
32094
32095         this.currentSize = this.el.getBox(true);
32096
32097         /// was window resize... - let's see if this works..
32098         Roo.EventManager.onWindowResize(this.resize, this); 
32099
32100         if(!this.isAutoInitial){
32101             this.layout();
32102             return;
32103         }
32104         
32105         this.layout.defer(500,this);
32106     },
32107     
32108     reloadItems: function()
32109     {
32110         this.bricks = this.el.select('.masonry-brick', true);
32111         
32112         this.bricks.each(function(b) {
32113             //Roo.log(b.getSize());
32114             if (!b.attr('originalwidth')) {
32115                 b.attr('originalwidth',  b.getSize().width);
32116             }
32117             
32118         });
32119         
32120         Roo.log(this.bricks.elements.length);
32121     },
32122     
32123     resize : function()
32124     {
32125         Roo.log('resize');
32126         var cs = this.el.getBox(true);
32127         
32128         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32129             Roo.log("no change in with or X");
32130             return;
32131         }
32132         this.currentSize = cs;
32133         this.layout();
32134     },
32135     
32136     layout : function()
32137     {
32138          Roo.log('layout');
32139         this._resetLayout();
32140         //this._manageStamps();
32141       
32142         // don't animate first layout
32143         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32144         this.layoutItems( isInstant );
32145       
32146         // flag for initalized
32147         this._isLayoutInited = true;
32148     },
32149     
32150     layoutItems : function( isInstant )
32151     {
32152         //var items = this._getItemsForLayout( this.items );
32153         // original code supports filtering layout items.. we just ignore it..
32154         
32155         this._layoutItems( this.bricks , isInstant );
32156       
32157         this._postLayout();
32158     },
32159     _layoutItems : function ( items , isInstant)
32160     {
32161        //this.fireEvent( 'layout', this, items );
32162     
32163
32164         if ( !items || !items.elements.length ) {
32165           // no items, emit event with empty array
32166             return;
32167         }
32168
32169         var queue = [];
32170         items.each(function(item) {
32171             Roo.log("layout item");
32172             Roo.log(item);
32173             // get x/y object from method
32174             var position = this._getItemLayoutPosition( item );
32175             // enqueue
32176             position.item = item;
32177             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32178             queue.push( position );
32179         }, this);
32180       
32181         this._processLayoutQueue( queue );
32182     },
32183     /** Sets position of item in DOM
32184     * @param {Element} item
32185     * @param {Number} x - horizontal position
32186     * @param {Number} y - vertical position
32187     * @param {Boolean} isInstant - disables transitions
32188     */
32189     _processLayoutQueue : function( queue )
32190     {
32191         for ( var i=0, len = queue.length; i < len; i++ ) {
32192             var obj = queue[i];
32193             obj.item.position('absolute');
32194             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32195         }
32196     },
32197       
32198     
32199     /**
32200     * Any logic you want to do after each layout,
32201     * i.e. size the container
32202     */
32203     _postLayout : function()
32204     {
32205         this.resizeContainer();
32206     },
32207     
32208     resizeContainer : function()
32209     {
32210         if ( !this.isResizingContainer ) {
32211             return;
32212         }
32213         var size = this._getContainerSize();
32214         if ( size ) {
32215             this.el.setSize(size.width,size.height);
32216             this.boxesEl.setSize(size.width,size.height);
32217         }
32218     },
32219     
32220     
32221     
32222     _resetLayout : function()
32223     {
32224         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32225         this.colWidth = this.el.getWidth();
32226         //this.gutter = this.el.getWidth(); 
32227         
32228         this.measureColumns();
32229
32230         // reset column Y
32231         var i = this.cols;
32232         this.colYs = [];
32233         while (i--) {
32234             this.colYs.push( 0 );
32235         }
32236     
32237         this.maxY = 0;
32238     },
32239
32240     measureColumns : function()
32241     {
32242         this.getContainerWidth();
32243       // if columnWidth is 0, default to outerWidth of first item
32244         if ( !this.columnWidth ) {
32245             var firstItem = this.bricks.first();
32246             Roo.log(firstItem);
32247             this.columnWidth  = this.containerWidth;
32248             if (firstItem && firstItem.attr('originalwidth') ) {
32249                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32250             }
32251             // columnWidth fall back to item of first element
32252             Roo.log("set column width?");
32253                         this.initialColumnWidth = this.columnWidth  ;
32254
32255             // if first elem has no width, default to size of container
32256             
32257         }
32258         
32259         
32260         if (this.initialColumnWidth) {
32261             this.columnWidth = this.initialColumnWidth;
32262         }
32263         
32264         
32265             
32266         // column width is fixed at the top - however if container width get's smaller we should
32267         // reduce it...
32268         
32269         // this bit calcs how man columns..
32270             
32271         var columnWidth = this.columnWidth += this.gutter;
32272       
32273         // calculate columns
32274         var containerWidth = this.containerWidth + this.gutter;
32275         
32276         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32277         // fix rounding errors, typically with gutters
32278         var excess = columnWidth - containerWidth % columnWidth;
32279         
32280         
32281         // if overshoot is less than a pixel, round up, otherwise floor it
32282         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32283         cols = Math[ mathMethod ]( cols );
32284         this.cols = Math.max( cols, 1 );
32285         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32286         
32287          // padding positioning..
32288         var totalColWidth = this.cols * this.columnWidth;
32289         var padavail = this.containerWidth - totalColWidth;
32290         // so for 2 columns - we need 3 'pads'
32291         
32292         var padNeeded = (1+this.cols) * this.padWidth;
32293         
32294         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32295         
32296         this.columnWidth += padExtra
32297         //this.padWidth = Math.floor(padavail /  ( this.cols));
32298         
32299         // adjust colum width so that padding is fixed??
32300         
32301         // we have 3 columns ... total = width * 3
32302         // we have X left over... that should be used by 
32303         
32304         //if (this.expandC) {
32305             
32306         //}
32307         
32308         
32309         
32310     },
32311     
32312     getContainerWidth : function()
32313     {
32314        /* // container is parent if fit width
32315         var container = this.isFitWidth ? this.element.parentNode : this.element;
32316         // check that this.size and size are there
32317         // IE8 triggers resize on body size change, so they might not be
32318         
32319         var size = getSize( container );  //FIXME
32320         this.containerWidth = size && size.innerWidth; //FIXME
32321         */
32322          
32323         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32324         
32325     },
32326     
32327     _getItemLayoutPosition : function( item )  // what is item?
32328     {
32329         // we resize the item to our columnWidth..
32330       
32331         item.setWidth(this.columnWidth);
32332         item.autoBoxAdjust  = false;
32333         
32334         var sz = item.getSize();
32335  
32336         // how many columns does this brick span
32337         var remainder = this.containerWidth % this.columnWidth;
32338         
32339         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32340         // round if off by 1 pixel, otherwise use ceil
32341         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32342         colSpan = Math.min( colSpan, this.cols );
32343         
32344         // normally this should be '1' as we dont' currently allow multi width columns..
32345         
32346         var colGroup = this._getColGroup( colSpan );
32347         // get the minimum Y value from the columns
32348         var minimumY = Math.min.apply( Math, colGroup );
32349         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32350         
32351         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32352          
32353         // position the brick
32354         var position = {
32355             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32356             y: this.currentSize.y + minimumY + this.padHeight
32357         };
32358         
32359         Roo.log(position);
32360         // apply setHeight to necessary columns
32361         var setHeight = minimumY + sz.height + this.padHeight;
32362         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32363         
32364         var setSpan = this.cols + 1 - colGroup.length;
32365         for ( var i = 0; i < setSpan; i++ ) {
32366           this.colYs[ shortColIndex + i ] = setHeight ;
32367         }
32368       
32369         return position;
32370     },
32371     
32372     /**
32373      * @param {Number} colSpan - number of columns the element spans
32374      * @returns {Array} colGroup
32375      */
32376     _getColGroup : function( colSpan )
32377     {
32378         if ( colSpan < 2 ) {
32379           // if brick spans only one column, use all the column Ys
32380           return this.colYs;
32381         }
32382       
32383         var colGroup = [];
32384         // how many different places could this brick fit horizontally
32385         var groupCount = this.cols + 1 - colSpan;
32386         // for each group potential horizontal position
32387         for ( var i = 0; i < groupCount; i++ ) {
32388           // make an array of colY values for that one group
32389           var groupColYs = this.colYs.slice( i, i + colSpan );
32390           // and get the max value of the array
32391           colGroup[i] = Math.max.apply( Math, groupColYs );
32392         }
32393         return colGroup;
32394     },
32395     /*
32396     _manageStamp : function( stamp )
32397     {
32398         var stampSize =  stamp.getSize();
32399         var offset = stamp.getBox();
32400         // get the columns that this stamp affects
32401         var firstX = this.isOriginLeft ? offset.x : offset.right;
32402         var lastX = firstX + stampSize.width;
32403         var firstCol = Math.floor( firstX / this.columnWidth );
32404         firstCol = Math.max( 0, firstCol );
32405         
32406         var lastCol = Math.floor( lastX / this.columnWidth );
32407         // lastCol should not go over if multiple of columnWidth #425
32408         lastCol -= lastX % this.columnWidth ? 0 : 1;
32409         lastCol = Math.min( this.cols - 1, lastCol );
32410         
32411         // set colYs to bottom of the stamp
32412         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32413             stampSize.height;
32414             
32415         for ( var i = firstCol; i <= lastCol; i++ ) {
32416           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32417         }
32418     },
32419     */
32420     
32421     _getContainerSize : function()
32422     {
32423         this.maxY = Math.max.apply( Math, this.colYs );
32424         var size = {
32425             height: this.maxY
32426         };
32427       
32428         if ( this.isFitWidth ) {
32429             size.width = this._getContainerFitWidth();
32430         }
32431       
32432         return size;
32433     },
32434     
32435     _getContainerFitWidth : function()
32436     {
32437         var unusedCols = 0;
32438         // count unused columns
32439         var i = this.cols;
32440         while ( --i ) {
32441           if ( this.colYs[i] !== 0 ) {
32442             break;
32443           }
32444           unusedCols++;
32445         }
32446         // fit container to columns that have been used
32447         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32448     },
32449     
32450     needsResizeLayout : function()
32451     {
32452         var previousWidth = this.containerWidth;
32453         this.getContainerWidth();
32454         return previousWidth !== this.containerWidth;
32455     }
32456  
32457 });
32458
32459  
32460
32461  /*
32462  * - LGPL
32463  *
32464  * element
32465  * 
32466  */
32467
32468 /**
32469  * @class Roo.bootstrap.MasonryBrick
32470  * @extends Roo.bootstrap.Component
32471  * Bootstrap MasonryBrick class
32472  * 
32473  * @constructor
32474  * Create a new MasonryBrick
32475  * @param {Object} config The config object
32476  */
32477
32478 Roo.bootstrap.MasonryBrick = function(config){
32479     
32480     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32481     
32482     Roo.bootstrap.MasonryBrick.register(this);
32483     
32484     this.addEvents({
32485         // raw events
32486         /**
32487          * @event click
32488          * When a MasonryBrick is clcik
32489          * @param {Roo.bootstrap.MasonryBrick} this
32490          * @param {Roo.EventObject} e
32491          */
32492         "click" : true
32493     });
32494 };
32495
32496 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32497     
32498     /**
32499      * @cfg {String} title
32500      */   
32501     title : '',
32502     /**
32503      * @cfg {String} html
32504      */   
32505     html : '',
32506     /**
32507      * @cfg {String} bgimage
32508      */   
32509     bgimage : '',
32510     /**
32511      * @cfg {String} videourl
32512      */   
32513     videourl : '',
32514     /**
32515      * @cfg {String} cls
32516      */   
32517     cls : '',
32518     /**
32519      * @cfg {String} href
32520      */   
32521     href : '',
32522     /**
32523      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32524      */   
32525     size : 'xs',
32526     
32527     /**
32528      * @cfg {String} placetitle (center|bottom)
32529      */   
32530     placetitle : '',
32531     
32532     /**
32533      * @cfg {Boolean} isFitContainer defalut true
32534      */   
32535     isFitContainer : true, 
32536     
32537     /**
32538      * @cfg {Boolean} preventDefault defalut false
32539      */   
32540     preventDefault : false, 
32541     
32542     /**
32543      * @cfg {Boolean} inverse defalut false
32544      */   
32545     maskInverse : false, 
32546     
32547     getAutoCreate : function()
32548     {
32549         if(!this.isFitContainer){
32550             return this.getSplitAutoCreate();
32551         }
32552         
32553         var cls = 'masonry-brick masonry-brick-full';
32554         
32555         if(this.href.length){
32556             cls += ' masonry-brick-link';
32557         }
32558         
32559         if(this.bgimage.length){
32560             cls += ' masonry-brick-image';
32561         }
32562         
32563         if(this.maskInverse){
32564             cls += ' mask-inverse';
32565         }
32566         
32567         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32568             cls += ' enable-mask';
32569         }
32570         
32571         if(this.size){
32572             cls += ' masonry-' + this.size + '-brick';
32573         }
32574         
32575         if(this.placetitle.length){
32576             
32577             switch (this.placetitle) {
32578                 case 'center' :
32579                     cls += ' masonry-center-title';
32580                     break;
32581                 case 'bottom' :
32582                     cls += ' masonry-bottom-title';
32583                     break;
32584                 default:
32585                     break;
32586             }
32587             
32588         } else {
32589             if(!this.html.length && !this.bgimage.length){
32590                 cls += ' masonry-center-title';
32591             }
32592
32593             if(!this.html.length && this.bgimage.length){
32594                 cls += ' masonry-bottom-title';
32595             }
32596         }
32597         
32598         if(this.cls){
32599             cls += ' ' + this.cls;
32600         }
32601         
32602         var cfg = {
32603             tag: (this.href.length) ? 'a' : 'div',
32604             cls: cls,
32605             cn: [
32606                 {
32607                     tag: 'div',
32608                     cls: 'masonry-brick-mask'
32609                 },
32610                 {
32611                     tag: 'div',
32612                     cls: 'masonry-brick-paragraph',
32613                     cn: []
32614                 }
32615             ]
32616         };
32617         
32618         if(this.href.length){
32619             cfg.href = this.href;
32620         }
32621         
32622         var cn = cfg.cn[1].cn;
32623         
32624         if(this.title.length){
32625             cn.push({
32626                 tag: 'h4',
32627                 cls: 'masonry-brick-title',
32628                 html: this.title
32629             });
32630         }
32631         
32632         if(this.html.length){
32633             cn.push({
32634                 tag: 'p',
32635                 cls: 'masonry-brick-text',
32636                 html: this.html
32637             });
32638         }
32639         
32640         if (!this.title.length && !this.html.length) {
32641             cfg.cn[1].cls += ' hide';
32642         }
32643         
32644         if(this.bgimage.length){
32645             cfg.cn.push({
32646                 tag: 'img',
32647                 cls: 'masonry-brick-image-view',
32648                 src: this.bgimage
32649             });
32650         }
32651         
32652         if(this.videourl.length){
32653             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32654             // youtube support only?
32655             cfg.cn.push({
32656                 tag: 'iframe',
32657                 cls: 'masonry-brick-image-view',
32658                 src: vurl,
32659                 frameborder : 0,
32660                 allowfullscreen : true
32661             });
32662         }
32663         
32664         return cfg;
32665         
32666     },
32667     
32668     getSplitAutoCreate : function()
32669     {
32670         var cls = 'masonry-brick masonry-brick-split';
32671         
32672         if(this.href.length){
32673             cls += ' masonry-brick-link';
32674         }
32675         
32676         if(this.bgimage.length){
32677             cls += ' masonry-brick-image';
32678         }
32679         
32680         if(this.size){
32681             cls += ' masonry-' + this.size + '-brick';
32682         }
32683         
32684         switch (this.placetitle) {
32685             case 'center' :
32686                 cls += ' masonry-center-title';
32687                 break;
32688             case 'bottom' :
32689                 cls += ' masonry-bottom-title';
32690                 break;
32691             default:
32692                 if(!this.bgimage.length){
32693                     cls += ' masonry-center-title';
32694                 }
32695
32696                 if(this.bgimage.length){
32697                     cls += ' masonry-bottom-title';
32698                 }
32699                 break;
32700         }
32701         
32702         if(this.cls){
32703             cls += ' ' + this.cls;
32704         }
32705         
32706         var cfg = {
32707             tag: (this.href.length) ? 'a' : 'div',
32708             cls: cls,
32709             cn: [
32710                 {
32711                     tag: 'div',
32712                     cls: 'masonry-brick-split-head',
32713                     cn: [
32714                         {
32715                             tag: 'div',
32716                             cls: 'masonry-brick-paragraph',
32717                             cn: []
32718                         }
32719                     ]
32720                 },
32721                 {
32722                     tag: 'div',
32723                     cls: 'masonry-brick-split-body',
32724                     cn: []
32725                 }
32726             ]
32727         };
32728         
32729         if(this.href.length){
32730             cfg.href = this.href;
32731         }
32732         
32733         if(this.title.length){
32734             cfg.cn[0].cn[0].cn.push({
32735                 tag: 'h4',
32736                 cls: 'masonry-brick-title',
32737                 html: this.title
32738             });
32739         }
32740         
32741         if(this.html.length){
32742             cfg.cn[1].cn.push({
32743                 tag: 'p',
32744                 cls: 'masonry-brick-text',
32745                 html: this.html
32746             });
32747         }
32748
32749         if(this.bgimage.length){
32750             cfg.cn[0].cn.push({
32751                 tag: 'img',
32752                 cls: 'masonry-brick-image-view',
32753                 src: this.bgimage
32754             });
32755         }
32756         
32757         if(this.videourl.length){
32758             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32759             // youtube support only?
32760             cfg.cn[0].cn.cn.push({
32761                 tag: 'iframe',
32762                 cls: 'masonry-brick-image-view',
32763                 src: vurl,
32764                 frameborder : 0,
32765                 allowfullscreen : true
32766             });
32767         }
32768         
32769         return cfg;
32770     },
32771     
32772     initEvents: function() 
32773     {
32774         switch (this.size) {
32775             case 'xs' :
32776                 this.x = 1;
32777                 this.y = 1;
32778                 break;
32779             case 'sm' :
32780                 this.x = 2;
32781                 this.y = 2;
32782                 break;
32783             case 'md' :
32784             case 'md-left' :
32785             case 'md-right' :
32786                 this.x = 3;
32787                 this.y = 3;
32788                 break;
32789             case 'tall' :
32790                 this.x = 2;
32791                 this.y = 3;
32792                 break;
32793             case 'wide' :
32794                 this.x = 3;
32795                 this.y = 2;
32796                 break;
32797             case 'wide-thin' :
32798                 this.x = 3;
32799                 this.y = 1;
32800                 break;
32801                         
32802             default :
32803                 break;
32804         }
32805         
32806         if(Roo.isTouch){
32807             this.el.on('touchstart', this.onTouchStart, this);
32808             this.el.on('touchmove', this.onTouchMove, this);
32809             this.el.on('touchend', this.onTouchEnd, this);
32810             this.el.on('contextmenu', this.onContextMenu, this);
32811         } else {
32812             this.el.on('mouseenter'  ,this.enter, this);
32813             this.el.on('mouseleave', this.leave, this);
32814             this.el.on('click', this.onClick, this);
32815         }
32816         
32817         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32818             this.parent().bricks.push(this);   
32819         }
32820         
32821     },
32822     
32823     onClick: function(e, el)
32824     {
32825         var time = this.endTimer - this.startTimer;
32826         // Roo.log(e.preventDefault());
32827         if(Roo.isTouch){
32828             if(time > 1000){
32829                 e.preventDefault();
32830                 return;
32831             }
32832         }
32833         
32834         if(!this.preventDefault){
32835             return;
32836         }
32837         
32838         e.preventDefault();
32839         
32840         if (this.activeClass != '') {
32841             this.selectBrick();
32842         }
32843         
32844         this.fireEvent('click', this, e);
32845     },
32846     
32847     enter: 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.9, true);
32857         }
32858     },
32859     
32860     leave: function(e, el)
32861     {
32862         e.preventDefault();
32863         
32864         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32865             return;
32866         }
32867         
32868         if(this.bgimage.length && this.html.length){
32869             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32870         }
32871     },
32872     
32873     onTouchStart: function(e, el)
32874     {
32875 //        e.preventDefault();
32876         
32877         this.touchmoved = false;
32878         
32879         if(!this.isFitContainer){
32880             return;
32881         }
32882         
32883         if(!this.bgimage.length || !this.html.length){
32884             return;
32885         }
32886         
32887         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32888         
32889         this.timer = new Date().getTime();
32890         
32891     },
32892     
32893     onTouchMove: function(e, el)
32894     {
32895         this.touchmoved = true;
32896     },
32897     
32898     onContextMenu : function(e,el)
32899     {
32900         e.preventDefault();
32901         e.stopPropagation();
32902         return false;
32903     },
32904     
32905     onTouchEnd: function(e, el)
32906     {
32907 //        e.preventDefault();
32908         
32909         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32910         
32911             this.leave(e,el);
32912             
32913             return;
32914         }
32915         
32916         if(!this.bgimage.length || !this.html.length){
32917             
32918             if(this.href.length){
32919                 window.location.href = this.href;
32920             }
32921             
32922             return;
32923         }
32924         
32925         if(!this.isFitContainer){
32926             return;
32927         }
32928         
32929         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32930         
32931         window.location.href = this.href;
32932     },
32933     
32934     //selection on single brick only
32935     selectBrick : function() {
32936         
32937         if (!this.parentId) {
32938             return;
32939         }
32940         
32941         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32942         var index = m.selectedBrick.indexOf(this.id);
32943         
32944         if ( index > -1) {
32945             m.selectedBrick.splice(index,1);
32946             this.el.removeClass(this.activeClass);
32947             return;
32948         }
32949         
32950         for(var i = 0; i < m.selectedBrick.length; i++) {
32951             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32952             b.el.removeClass(b.activeClass);
32953         }
32954         
32955         m.selectedBrick = [];
32956         
32957         m.selectedBrick.push(this.id);
32958         this.el.addClass(this.activeClass);
32959         return;
32960     },
32961     
32962     isSelected : function(){
32963         return this.el.hasClass(this.activeClass);
32964         
32965     }
32966 });
32967
32968 Roo.apply(Roo.bootstrap.MasonryBrick, {
32969     
32970     //groups: {},
32971     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32972      /**
32973     * register a Masonry Brick
32974     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32975     */
32976     
32977     register : function(brick)
32978     {
32979         //this.groups[brick.id] = brick;
32980         this.groups.add(brick.id, brick);
32981     },
32982     /**
32983     * fetch a  masonry brick based on the masonry brick ID
32984     * @param {string} the masonry brick to add
32985     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32986     */
32987     
32988     get: function(brick_id) 
32989     {
32990         // if (typeof(this.groups[brick_id]) == 'undefined') {
32991         //     return false;
32992         // }
32993         // return this.groups[brick_id] ;
32994         
32995         if(this.groups.key(brick_id)) {
32996             return this.groups.key(brick_id);
32997         }
32998         
32999         return false;
33000     }
33001     
33002     
33003     
33004 });
33005
33006  /*
33007  * - LGPL
33008  *
33009  * element
33010  * 
33011  */
33012
33013 /**
33014  * @class Roo.bootstrap.Brick
33015  * @extends Roo.bootstrap.Component
33016  * Bootstrap Brick class
33017  * 
33018  * @constructor
33019  * Create a new Brick
33020  * @param {Object} config The config object
33021  */
33022
33023 Roo.bootstrap.Brick = function(config){
33024     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33025     
33026     this.addEvents({
33027         // raw events
33028         /**
33029          * @event click
33030          * When a Brick is click
33031          * @param {Roo.bootstrap.Brick} this
33032          * @param {Roo.EventObject} e
33033          */
33034         "click" : true
33035     });
33036 };
33037
33038 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33039     
33040     /**
33041      * @cfg {String} title
33042      */   
33043     title : '',
33044     /**
33045      * @cfg {String} html
33046      */   
33047     html : '',
33048     /**
33049      * @cfg {String} bgimage
33050      */   
33051     bgimage : '',
33052     /**
33053      * @cfg {String} cls
33054      */   
33055     cls : '',
33056     /**
33057      * @cfg {String} href
33058      */   
33059     href : '',
33060     /**
33061      * @cfg {String} video
33062      */   
33063     video : '',
33064     /**
33065      * @cfg {Boolean} square
33066      */   
33067     square : true,
33068     
33069     getAutoCreate : function()
33070     {
33071         var cls = 'roo-brick';
33072         
33073         if(this.href.length){
33074             cls += ' roo-brick-link';
33075         }
33076         
33077         if(this.bgimage.length){
33078             cls += ' roo-brick-image';
33079         }
33080         
33081         if(!this.html.length && !this.bgimage.length){
33082             cls += ' roo-brick-center-title';
33083         }
33084         
33085         if(!this.html.length && this.bgimage.length){
33086             cls += ' roo-brick-bottom-title';
33087         }
33088         
33089         if(this.cls){
33090             cls += ' ' + this.cls;
33091         }
33092         
33093         var cfg = {
33094             tag: (this.href.length) ? 'a' : 'div',
33095             cls: cls,
33096             cn: [
33097                 {
33098                     tag: 'div',
33099                     cls: 'roo-brick-paragraph',
33100                     cn: []
33101                 }
33102             ]
33103         };
33104         
33105         if(this.href.length){
33106             cfg.href = this.href;
33107         }
33108         
33109         var cn = cfg.cn[0].cn;
33110         
33111         if(this.title.length){
33112             cn.push({
33113                 tag: 'h4',
33114                 cls: 'roo-brick-title',
33115                 html: this.title
33116             });
33117         }
33118         
33119         if(this.html.length){
33120             cn.push({
33121                 tag: 'p',
33122                 cls: 'roo-brick-text',
33123                 html: this.html
33124             });
33125         } else {
33126             cn.cls += ' hide';
33127         }
33128         
33129         if(this.bgimage.length){
33130             cfg.cn.push({
33131                 tag: 'img',
33132                 cls: 'roo-brick-image-view',
33133                 src: this.bgimage
33134             });
33135         }
33136         
33137         return cfg;
33138     },
33139     
33140     initEvents: function() 
33141     {
33142         if(this.title.length || this.html.length){
33143             this.el.on('mouseenter'  ,this.enter, this);
33144             this.el.on('mouseleave', this.leave, this);
33145         }
33146         
33147         Roo.EventManager.onWindowResize(this.resize, this); 
33148         
33149         if(this.bgimage.length){
33150             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33151             this.imageEl.on('load', this.onImageLoad, this);
33152             return;
33153         }
33154         
33155         this.resize();
33156     },
33157     
33158     onImageLoad : function()
33159     {
33160         this.resize();
33161     },
33162     
33163     resize : function()
33164     {
33165         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33166         
33167         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33168         
33169         if(this.bgimage.length){
33170             var image = this.el.select('.roo-brick-image-view', true).first();
33171             
33172             image.setWidth(paragraph.getWidth());
33173             
33174             if(this.square){
33175                 image.setHeight(paragraph.getWidth());
33176             }
33177             
33178             this.el.setHeight(image.getHeight());
33179             paragraph.setHeight(image.getHeight());
33180             
33181         }
33182         
33183     },
33184     
33185     enter: function(e, el)
33186     {
33187         e.preventDefault();
33188         
33189         if(this.bgimage.length){
33190             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33191             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33192         }
33193     },
33194     
33195     leave: function(e, el)
33196     {
33197         e.preventDefault();
33198         
33199         if(this.bgimage.length){
33200             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33201             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33202         }
33203     }
33204     
33205 });
33206
33207  
33208
33209  /*
33210  * - LGPL
33211  *
33212  * Number field 
33213  */
33214
33215 /**
33216  * @class Roo.bootstrap.NumberField
33217  * @extends Roo.bootstrap.Input
33218  * Bootstrap NumberField class
33219  * 
33220  * 
33221  * 
33222  * 
33223  * @constructor
33224  * Create a new NumberField
33225  * @param {Object} config The config object
33226  */
33227
33228 Roo.bootstrap.NumberField = function(config){
33229     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33230 };
33231
33232 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33233     
33234     /**
33235      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33236      */
33237     allowDecimals : true,
33238     /**
33239      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33240      */
33241     decimalSeparator : ".",
33242     /**
33243      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33244      */
33245     decimalPrecision : 2,
33246     /**
33247      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33248      */
33249     allowNegative : true,
33250     
33251     /**
33252      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33253      */
33254     allowZero: true,
33255     /**
33256      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33257      */
33258     minValue : Number.NEGATIVE_INFINITY,
33259     /**
33260      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33261      */
33262     maxValue : Number.MAX_VALUE,
33263     /**
33264      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33265      */
33266     minText : "The minimum value for this field is {0}",
33267     /**
33268      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33269      */
33270     maxText : "The maximum value for this field is {0}",
33271     /**
33272      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33273      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33274      */
33275     nanText : "{0} is not a valid number",
33276     /**
33277      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33278      */
33279     thousandsDelimiter : false,
33280     /**
33281      * @cfg {String} valueAlign alignment of value
33282      */
33283     valueAlign : "left",
33284
33285     getAutoCreate : function()
33286     {
33287         var hiddenInput = {
33288             tag: 'input',
33289             type: 'hidden',
33290             id: Roo.id(),
33291             cls: 'hidden-number-input'
33292         };
33293         
33294         if (this.name) {
33295             hiddenInput.name = this.name;
33296         }
33297         
33298         this.name = '';
33299         
33300         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33301         
33302         this.name = hiddenInput.name;
33303         
33304         if(cfg.cn.length > 0) {
33305             cfg.cn.push(hiddenInput);
33306         }
33307         
33308         return cfg;
33309     },
33310
33311     // private
33312     initEvents : function()
33313     {   
33314         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33315         
33316         var allowed = "0123456789";
33317         
33318         if(this.allowDecimals){
33319             allowed += this.decimalSeparator;
33320         }
33321         
33322         if(this.allowNegative){
33323             allowed += "-";
33324         }
33325         
33326         if(this.thousandsDelimiter) {
33327             allowed += ",";
33328         }
33329         
33330         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33331         
33332         var keyPress = function(e){
33333             
33334             var k = e.getKey();
33335             
33336             var c = e.getCharCode();
33337             
33338             if(
33339                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33340                     allowed.indexOf(String.fromCharCode(c)) === -1
33341             ){
33342                 e.stopEvent();
33343                 return;
33344             }
33345             
33346             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33347                 return;
33348             }
33349             
33350             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33351                 e.stopEvent();
33352             }
33353         };
33354         
33355         this.el.on("keypress", keyPress, this);
33356     },
33357     
33358     validateValue : function(value)
33359     {
33360         
33361         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33362             return false;
33363         }
33364         
33365         var num = this.parseValue(value);
33366         
33367         if(isNaN(num)){
33368             this.markInvalid(String.format(this.nanText, value));
33369             return false;
33370         }
33371         
33372         if(num < this.minValue){
33373             this.markInvalid(String.format(this.minText, this.minValue));
33374             return false;
33375         }
33376         
33377         if(num > this.maxValue){
33378             this.markInvalid(String.format(this.maxText, this.maxValue));
33379             return false;
33380         }
33381         
33382         return true;
33383     },
33384
33385     getValue : function()
33386     {
33387         var v = this.hiddenEl().getValue();
33388         
33389         return this.fixPrecision(this.parseValue(v));
33390     },
33391
33392     parseValue : function(value)
33393     {
33394         if(this.thousandsDelimiter) {
33395             value += "";
33396             r = new RegExp(",", "g");
33397             value = value.replace(r, "");
33398         }
33399         
33400         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33401         return isNaN(value) ? '' : value;
33402     },
33403
33404     fixPrecision : function(value)
33405     {
33406         if(this.thousandsDelimiter) {
33407             value += "";
33408             r = new RegExp(",", "g");
33409             value = value.replace(r, "");
33410         }
33411         
33412         var nan = isNaN(value);
33413         
33414         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33415             return nan ? '' : value;
33416         }
33417         return parseFloat(value).toFixed(this.decimalPrecision);
33418     },
33419
33420     setValue : function(v)
33421     {
33422         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33423         
33424         this.value = v;
33425         
33426         if(this.rendered){
33427             
33428             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33429             
33430             this.inputEl().dom.value = (v == '') ? '' :
33431                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33432             
33433             if(!this.allowZero && v === '0') {
33434                 this.hiddenEl().dom.value = '';
33435                 this.inputEl().dom.value = '';
33436             }
33437             
33438             this.validate();
33439         }
33440     },
33441
33442     decimalPrecisionFcn : function(v)
33443     {
33444         return Math.floor(v);
33445     },
33446
33447     beforeBlur : function()
33448     {
33449         var v = this.parseValue(this.getRawValue());
33450         
33451         if(v || v === 0 || v === ''){
33452             this.setValue(v);
33453         }
33454     },
33455     
33456     hiddenEl : function()
33457     {
33458         return this.el.select('input.hidden-number-input',true).first();
33459     }
33460     
33461 });
33462
33463  
33464
33465 /*
33466 * Licence: LGPL
33467 */
33468
33469 /**
33470  * @class Roo.bootstrap.DocumentSlider
33471  * @extends Roo.bootstrap.Component
33472  * Bootstrap DocumentSlider class
33473  * 
33474  * @constructor
33475  * Create a new DocumentViewer
33476  * @param {Object} config The config object
33477  */
33478
33479 Roo.bootstrap.DocumentSlider = function(config){
33480     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33481     
33482     this.files = [];
33483     
33484     this.addEvents({
33485         /**
33486          * @event initial
33487          * Fire after initEvent
33488          * @param {Roo.bootstrap.DocumentSlider} this
33489          */
33490         "initial" : true,
33491         /**
33492          * @event update
33493          * Fire after update
33494          * @param {Roo.bootstrap.DocumentSlider} this
33495          */
33496         "update" : true,
33497         /**
33498          * @event click
33499          * Fire after click
33500          * @param {Roo.bootstrap.DocumentSlider} this
33501          */
33502         "click" : true
33503     });
33504 };
33505
33506 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33507     
33508     files : false,
33509     
33510     indicator : 0,
33511     
33512     getAutoCreate : function()
33513     {
33514         var cfg = {
33515             tag : 'div',
33516             cls : 'roo-document-slider',
33517             cn : [
33518                 {
33519                     tag : 'div',
33520                     cls : 'roo-document-slider-header',
33521                     cn : [
33522                         {
33523                             tag : 'div',
33524                             cls : 'roo-document-slider-header-title'
33525                         }
33526                     ]
33527                 },
33528                 {
33529                     tag : 'div',
33530                     cls : 'roo-document-slider-body',
33531                     cn : [
33532                         {
33533                             tag : 'div',
33534                             cls : 'roo-document-slider-prev',
33535                             cn : [
33536                                 {
33537                                     tag : 'i',
33538                                     cls : 'fa fa-chevron-left'
33539                                 }
33540                             ]
33541                         },
33542                         {
33543                             tag : 'div',
33544                             cls : 'roo-document-slider-thumb',
33545                             cn : [
33546                                 {
33547                                     tag : 'img',
33548                                     cls : 'roo-document-slider-image'
33549                                 }
33550                             ]
33551                         },
33552                         {
33553                             tag : 'div',
33554                             cls : 'roo-document-slider-next',
33555                             cn : [
33556                                 {
33557                                     tag : 'i',
33558                                     cls : 'fa fa-chevron-right'
33559                                 }
33560                             ]
33561                         }
33562                     ]
33563                 }
33564             ]
33565         };
33566         
33567         return cfg;
33568     },
33569     
33570     initEvents : function()
33571     {
33572         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33573         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33574         
33575         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33576         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33577         
33578         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33579         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33580         
33581         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33582         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33583         
33584         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33585         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33586         
33587         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33588         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33589         
33590         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33591         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33592         
33593         this.thumbEl.on('click', this.onClick, this);
33594         
33595         this.prevIndicator.on('click', this.prev, this);
33596         
33597         this.nextIndicator.on('click', this.next, this);
33598         
33599     },
33600     
33601     initial : function()
33602     {
33603         if(this.files.length){
33604             this.indicator = 1;
33605             this.update()
33606         }
33607         
33608         this.fireEvent('initial', this);
33609     },
33610     
33611     update : function()
33612     {
33613         this.imageEl.attr('src', this.files[this.indicator - 1]);
33614         
33615         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33616         
33617         this.prevIndicator.show();
33618         
33619         if(this.indicator == 1){
33620             this.prevIndicator.hide();
33621         }
33622         
33623         this.nextIndicator.show();
33624         
33625         if(this.indicator == this.files.length){
33626             this.nextIndicator.hide();
33627         }
33628         
33629         this.thumbEl.scrollTo('top');
33630         
33631         this.fireEvent('update', this);
33632     },
33633     
33634     onClick : function(e)
33635     {
33636         e.preventDefault();
33637         
33638         this.fireEvent('click', this);
33639     },
33640     
33641     prev : function(e)
33642     {
33643         e.preventDefault();
33644         
33645         this.indicator = Math.max(1, this.indicator - 1);
33646         
33647         this.update();
33648     },
33649     
33650     next : function(e)
33651     {
33652         e.preventDefault();
33653         
33654         this.indicator = Math.min(this.files.length, this.indicator + 1);
33655         
33656         this.update();
33657     }
33658 });
33659 /*
33660  * - LGPL
33661  *
33662  * RadioSet
33663  *
33664  *
33665  */
33666
33667 /**
33668  * @class Roo.bootstrap.RadioSet
33669  * @extends Roo.bootstrap.Input
33670  * Bootstrap RadioSet class
33671  * @cfg {String} indicatorpos (left|right) default left
33672  * @cfg {Boolean} inline (true|false) inline the element (default true)
33673  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33674  * @constructor
33675  * Create a new RadioSet
33676  * @param {Object} config The config object
33677  */
33678
33679 Roo.bootstrap.RadioSet = function(config){
33680     
33681     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33682     
33683     this.radioes = [];
33684     
33685     Roo.bootstrap.RadioSet.register(this);
33686     
33687     this.addEvents({
33688         /**
33689         * @event check
33690         * Fires when the element is checked or unchecked.
33691         * @param {Roo.bootstrap.RadioSet} this This radio
33692         * @param {Roo.bootstrap.Radio} item The checked item
33693         */
33694        check : true,
33695        /**
33696         * @event click
33697         * Fires when the element is click.
33698         * @param {Roo.bootstrap.RadioSet} this This radio set
33699         * @param {Roo.bootstrap.Radio} item The checked item
33700         * @param {Roo.EventObject} e The event object
33701         */
33702        click : true
33703     });
33704     
33705 };
33706
33707 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33708
33709     radioes : false,
33710     
33711     inline : true,
33712     
33713     weight : '',
33714     
33715     indicatorpos : 'left',
33716     
33717     getAutoCreate : function()
33718     {
33719         var label = {
33720             tag : 'label',
33721             cls : 'roo-radio-set-label',
33722             cn : [
33723                 {
33724                     tag : 'span',
33725                     html : this.fieldLabel
33726                 }
33727             ]
33728         };
33729         
33730         if(this.indicatorpos == 'left'){
33731             label.cn.unshift({
33732                 tag : 'i',
33733                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33734                 tooltip : 'This field is required'
33735             });
33736         } else {
33737             label.cn.push({
33738                 tag : 'i',
33739                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33740                 tooltip : 'This field is required'
33741             });
33742         }
33743         
33744         var items = {
33745             tag : 'div',
33746             cls : 'roo-radio-set-items'
33747         };
33748         
33749         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33750         
33751         if (align === 'left' && this.fieldLabel.length) {
33752             
33753             items = {
33754                 cls : "roo-radio-set-right", 
33755                 cn: [
33756                     items
33757                 ]
33758             };
33759             
33760             if(this.labelWidth > 12){
33761                 label.style = "width: " + this.labelWidth + 'px';
33762             }
33763             
33764             if(this.labelWidth < 13 && this.labelmd == 0){
33765                 this.labelmd = this.labelWidth;
33766             }
33767             
33768             if(this.labellg > 0){
33769                 label.cls += ' col-lg-' + this.labellg;
33770                 items.cls += ' col-lg-' + (12 - this.labellg);
33771             }
33772             
33773             if(this.labelmd > 0){
33774                 label.cls += ' col-md-' + this.labelmd;
33775                 items.cls += ' col-md-' + (12 - this.labelmd);
33776             }
33777             
33778             if(this.labelsm > 0){
33779                 label.cls += ' col-sm-' + this.labelsm;
33780                 items.cls += ' col-sm-' + (12 - this.labelsm);
33781             }
33782             
33783             if(this.labelxs > 0){
33784                 label.cls += ' col-xs-' + this.labelxs;
33785                 items.cls += ' col-xs-' + (12 - this.labelxs);
33786             }
33787         }
33788         
33789         var cfg = {
33790             tag : 'div',
33791             cls : 'roo-radio-set',
33792             cn : [
33793                 {
33794                     tag : 'input',
33795                     cls : 'roo-radio-set-input',
33796                     type : 'hidden',
33797                     name : this.name,
33798                     value : this.value ? this.value :  ''
33799                 },
33800                 label,
33801                 items
33802             ]
33803         };
33804         
33805         if(this.weight.length){
33806             cfg.cls += ' roo-radio-' + this.weight;
33807         }
33808         
33809         if(this.inline) {
33810             cfg.cls += ' roo-radio-set-inline';
33811         }
33812         
33813         var settings=this;
33814         ['xs','sm','md','lg'].map(function(size){
33815             if (settings[size]) {
33816                 cfg.cls += ' col-' + size + '-' + settings[size];
33817             }
33818         });
33819         
33820         return cfg;
33821         
33822     },
33823
33824     initEvents : function()
33825     {
33826         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33827         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33828         
33829         if(!this.fieldLabel.length){
33830             this.labelEl.hide();
33831         }
33832         
33833         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33834         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33835         
33836         this.indicator = this.indicatorEl();
33837         
33838         if(this.indicator){
33839             this.indicator.addClass('invisible');
33840         }
33841         
33842         this.originalValue = this.getValue();
33843         
33844     },
33845     
33846     inputEl: function ()
33847     {
33848         return this.el.select('.roo-radio-set-input', true).first();
33849     },
33850     
33851     getChildContainer : function()
33852     {
33853         return this.itemsEl;
33854     },
33855     
33856     register : function(item)
33857     {
33858         this.radioes.push(item);
33859         
33860     },
33861     
33862     validate : function()
33863     {   
33864         if(this.getVisibilityEl().hasClass('hidden')){
33865             return true;
33866         }
33867         
33868         var valid = false;
33869         
33870         Roo.each(this.radioes, function(i){
33871             if(!i.checked){
33872                 return;
33873             }
33874             
33875             valid = true;
33876             return false;
33877         });
33878         
33879         if(this.allowBlank) {
33880             return true;
33881         }
33882         
33883         if(this.disabled || valid){
33884             this.markValid();
33885             return true;
33886         }
33887         
33888         this.markInvalid();
33889         return false;
33890         
33891     },
33892     
33893     markValid : function()
33894     {
33895         if(this.labelEl.isVisible(true)){
33896             this.indicatorEl().removeClass('visible');
33897             this.indicatorEl().addClass('invisible');
33898         }
33899         
33900         this.el.removeClass([this.invalidClass, this.validClass]);
33901         this.el.addClass(this.validClass);
33902         
33903         this.fireEvent('valid', this);
33904     },
33905     
33906     markInvalid : function(msg)
33907     {
33908         if(this.allowBlank || this.disabled){
33909             return;
33910         }
33911         
33912         if(this.labelEl.isVisible(true)){
33913             this.indicatorEl().removeClass('invisible');
33914             this.indicatorEl().addClass('visible');
33915         }
33916         
33917         this.el.removeClass([this.invalidClass, this.validClass]);
33918         this.el.addClass(this.invalidClass);
33919         
33920         this.fireEvent('invalid', this, msg);
33921         
33922     },
33923     
33924     setValue : function(v, suppressEvent)
33925     {   
33926         if(this.value === v){
33927             return;
33928         }
33929         
33930         this.value = v;
33931         
33932         if(this.rendered){
33933             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33934         }
33935         
33936         Roo.each(this.radioes, function(i){
33937             i.checked = false;
33938             i.el.removeClass('checked');
33939         });
33940         
33941         Roo.each(this.radioes, function(i){
33942             
33943             if(i.value === v || i.value.toString() === v.toString()){
33944                 i.checked = true;
33945                 i.el.addClass('checked');
33946                 
33947                 if(suppressEvent !== true){
33948                     this.fireEvent('check', this, i);
33949                 }
33950                 
33951                 return false;
33952             }
33953             
33954         }, this);
33955         
33956         this.validate();
33957     },
33958     
33959     clearInvalid : function(){
33960         
33961         if(!this.el || this.preventMark){
33962             return;
33963         }
33964         
33965         this.el.removeClass([this.invalidClass]);
33966         
33967         this.fireEvent('valid', this);
33968     }
33969     
33970 });
33971
33972 Roo.apply(Roo.bootstrap.RadioSet, {
33973     
33974     groups: {},
33975     
33976     register : function(set)
33977     {
33978         this.groups[set.name] = set;
33979     },
33980     
33981     get: function(name) 
33982     {
33983         if (typeof(this.groups[name]) == 'undefined') {
33984             return false;
33985         }
33986         
33987         return this.groups[name] ;
33988     }
33989     
33990 });
33991 /*
33992  * Based on:
33993  * Ext JS Library 1.1.1
33994  * Copyright(c) 2006-2007, Ext JS, LLC.
33995  *
33996  * Originally Released Under LGPL - original licence link has changed is not relivant.
33997  *
33998  * Fork - LGPL
33999  * <script type="text/javascript">
34000  */
34001
34002
34003 /**
34004  * @class Roo.bootstrap.SplitBar
34005  * @extends Roo.util.Observable
34006  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34007  * <br><br>
34008  * Usage:
34009  * <pre><code>
34010 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34011                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34012 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34013 split.minSize = 100;
34014 split.maxSize = 600;
34015 split.animate = true;
34016 split.on('moved', splitterMoved);
34017 </code></pre>
34018  * @constructor
34019  * Create a new SplitBar
34020  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34021  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34022  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34023  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34024                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34025                         position of the SplitBar).
34026  */
34027 Roo.bootstrap.SplitBar = function(cfg){
34028     
34029     /** @private */
34030     
34031     //{
34032     //  dragElement : elm
34033     //  resizingElement: el,
34034         // optional..
34035     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34036     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34037         // existingProxy ???
34038     //}
34039     
34040     this.el = Roo.get(cfg.dragElement, true);
34041     this.el.dom.unselectable = "on";
34042     /** @private */
34043     this.resizingEl = Roo.get(cfg.resizingElement, true);
34044
34045     /**
34046      * @private
34047      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34048      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34049      * @type Number
34050      */
34051     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34052     
34053     /**
34054      * The minimum size of the resizing element. (Defaults to 0)
34055      * @type Number
34056      */
34057     this.minSize = 0;
34058     
34059     /**
34060      * The maximum size of the resizing element. (Defaults to 2000)
34061      * @type Number
34062      */
34063     this.maxSize = 2000;
34064     
34065     /**
34066      * Whether to animate the transition to the new size
34067      * @type Boolean
34068      */
34069     this.animate = false;
34070     
34071     /**
34072      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34073      * @type Boolean
34074      */
34075     this.useShim = false;
34076     
34077     /** @private */
34078     this.shim = null;
34079     
34080     if(!cfg.existingProxy){
34081         /** @private */
34082         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34083     }else{
34084         this.proxy = Roo.get(cfg.existingProxy).dom;
34085     }
34086     /** @private */
34087     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34088     
34089     /** @private */
34090     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34091     
34092     /** @private */
34093     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34094     
34095     /** @private */
34096     this.dragSpecs = {};
34097     
34098     /**
34099      * @private The adapter to use to positon and resize elements
34100      */
34101     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34102     this.adapter.init(this);
34103     
34104     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34105         /** @private */
34106         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34107         this.el.addClass("roo-splitbar-h");
34108     }else{
34109         /** @private */
34110         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34111         this.el.addClass("roo-splitbar-v");
34112     }
34113     
34114     this.addEvents({
34115         /**
34116          * @event resize
34117          * Fires when the splitter is moved (alias for {@link #event-moved})
34118          * @param {Roo.bootstrap.SplitBar} this
34119          * @param {Number} newSize the new width or height
34120          */
34121         "resize" : true,
34122         /**
34123          * @event moved
34124          * Fires when the splitter is moved
34125          * @param {Roo.bootstrap.SplitBar} this
34126          * @param {Number} newSize the new width or height
34127          */
34128         "moved" : true,
34129         /**
34130          * @event beforeresize
34131          * Fires before the splitter is dragged
34132          * @param {Roo.bootstrap.SplitBar} this
34133          */
34134         "beforeresize" : true,
34135
34136         "beforeapply" : true
34137     });
34138
34139     Roo.util.Observable.call(this);
34140 };
34141
34142 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34143     onStartProxyDrag : function(x, y){
34144         this.fireEvent("beforeresize", this);
34145         if(!this.overlay){
34146             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34147             o.unselectable();
34148             o.enableDisplayMode("block");
34149             // all splitbars share the same overlay
34150             Roo.bootstrap.SplitBar.prototype.overlay = o;
34151         }
34152         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34153         this.overlay.show();
34154         Roo.get(this.proxy).setDisplayed("block");
34155         var size = this.adapter.getElementSize(this);
34156         this.activeMinSize = this.getMinimumSize();;
34157         this.activeMaxSize = this.getMaximumSize();;
34158         var c1 = size - this.activeMinSize;
34159         var c2 = Math.max(this.activeMaxSize - size, 0);
34160         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34161             this.dd.resetConstraints();
34162             this.dd.setXConstraint(
34163                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34164                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34165             );
34166             this.dd.setYConstraint(0, 0);
34167         }else{
34168             this.dd.resetConstraints();
34169             this.dd.setXConstraint(0, 0);
34170             this.dd.setYConstraint(
34171                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34172                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34173             );
34174          }
34175         this.dragSpecs.startSize = size;
34176         this.dragSpecs.startPoint = [x, y];
34177         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34178     },
34179     
34180     /** 
34181      * @private Called after the drag operation by the DDProxy
34182      */
34183     onEndProxyDrag : function(e){
34184         Roo.get(this.proxy).setDisplayed(false);
34185         var endPoint = Roo.lib.Event.getXY(e);
34186         if(this.overlay){
34187             this.overlay.hide();
34188         }
34189         var newSize;
34190         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34191             newSize = this.dragSpecs.startSize + 
34192                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34193                     endPoint[0] - this.dragSpecs.startPoint[0] :
34194                     this.dragSpecs.startPoint[0] - endPoint[0]
34195                 );
34196         }else{
34197             newSize = this.dragSpecs.startSize + 
34198                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34199                     endPoint[1] - this.dragSpecs.startPoint[1] :
34200                     this.dragSpecs.startPoint[1] - endPoint[1]
34201                 );
34202         }
34203         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34204         if(newSize != this.dragSpecs.startSize){
34205             if(this.fireEvent('beforeapply', this, newSize) !== false){
34206                 this.adapter.setElementSize(this, newSize);
34207                 this.fireEvent("moved", this, newSize);
34208                 this.fireEvent("resize", this, newSize);
34209             }
34210         }
34211     },
34212     
34213     /**
34214      * Get the adapter this SplitBar uses
34215      * @return The adapter object
34216      */
34217     getAdapter : function(){
34218         return this.adapter;
34219     },
34220     
34221     /**
34222      * Set the adapter this SplitBar uses
34223      * @param {Object} adapter A SplitBar adapter object
34224      */
34225     setAdapter : function(adapter){
34226         this.adapter = adapter;
34227         this.adapter.init(this);
34228     },
34229     
34230     /**
34231      * Gets the minimum size for the resizing element
34232      * @return {Number} The minimum size
34233      */
34234     getMinimumSize : function(){
34235         return this.minSize;
34236     },
34237     
34238     /**
34239      * Sets the minimum size for the resizing element
34240      * @param {Number} minSize The minimum size
34241      */
34242     setMinimumSize : function(minSize){
34243         this.minSize = minSize;
34244     },
34245     
34246     /**
34247      * Gets the maximum size for the resizing element
34248      * @return {Number} The maximum size
34249      */
34250     getMaximumSize : function(){
34251         return this.maxSize;
34252     },
34253     
34254     /**
34255      * Sets the maximum size for the resizing element
34256      * @param {Number} maxSize The maximum size
34257      */
34258     setMaximumSize : function(maxSize){
34259         this.maxSize = maxSize;
34260     },
34261     
34262     /**
34263      * Sets the initialize size for the resizing element
34264      * @param {Number} size The initial size
34265      */
34266     setCurrentSize : function(size){
34267         var oldAnimate = this.animate;
34268         this.animate = false;
34269         this.adapter.setElementSize(this, size);
34270         this.animate = oldAnimate;
34271     },
34272     
34273     /**
34274      * Destroy this splitbar. 
34275      * @param {Boolean} removeEl True to remove the element
34276      */
34277     destroy : function(removeEl){
34278         if(this.shim){
34279             this.shim.remove();
34280         }
34281         this.dd.unreg();
34282         this.proxy.parentNode.removeChild(this.proxy);
34283         if(removeEl){
34284             this.el.remove();
34285         }
34286     }
34287 });
34288
34289 /**
34290  * @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.
34291  */
34292 Roo.bootstrap.SplitBar.createProxy = function(dir){
34293     var proxy = new Roo.Element(document.createElement("div"));
34294     proxy.unselectable();
34295     var cls = 'roo-splitbar-proxy';
34296     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34297     document.body.appendChild(proxy.dom);
34298     return proxy.dom;
34299 };
34300
34301 /** 
34302  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34303  * Default Adapter. It assumes the splitter and resizing element are not positioned
34304  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34305  */
34306 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34307 };
34308
34309 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34310     // do nothing for now
34311     init : function(s){
34312     
34313     },
34314     /**
34315      * Called before drag operations to get the current size of the resizing element. 
34316      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34317      */
34318      getElementSize : function(s){
34319         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34320             return s.resizingEl.getWidth();
34321         }else{
34322             return s.resizingEl.getHeight();
34323         }
34324     },
34325     
34326     /**
34327      * Called after drag operations to set the size of the resizing element.
34328      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34329      * @param {Number} newSize The new size to set
34330      * @param {Function} onComplete A function to be invoked when resizing is complete
34331      */
34332     setElementSize : function(s, newSize, onComplete){
34333         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34334             if(!s.animate){
34335                 s.resizingEl.setWidth(newSize);
34336                 if(onComplete){
34337                     onComplete(s, newSize);
34338                 }
34339             }else{
34340                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34341             }
34342         }else{
34343             
34344             if(!s.animate){
34345                 s.resizingEl.setHeight(newSize);
34346                 if(onComplete){
34347                     onComplete(s, newSize);
34348                 }
34349             }else{
34350                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34351             }
34352         }
34353     }
34354 };
34355
34356 /** 
34357  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34358  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34359  * Adapter that  moves the splitter element to align with the resized sizing element. 
34360  * Used with an absolute positioned SplitBar.
34361  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34362  * document.body, make sure you assign an id to the body element.
34363  */
34364 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34365     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34366     this.container = Roo.get(container);
34367 };
34368
34369 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34370     init : function(s){
34371         this.basic.init(s);
34372     },
34373     
34374     getElementSize : function(s){
34375         return this.basic.getElementSize(s);
34376     },
34377     
34378     setElementSize : function(s, newSize, onComplete){
34379         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34380     },
34381     
34382     moveSplitter : function(s){
34383         var yes = Roo.bootstrap.SplitBar;
34384         switch(s.placement){
34385             case yes.LEFT:
34386                 s.el.setX(s.resizingEl.getRight());
34387                 break;
34388             case yes.RIGHT:
34389                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34390                 break;
34391             case yes.TOP:
34392                 s.el.setY(s.resizingEl.getBottom());
34393                 break;
34394             case yes.BOTTOM:
34395                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34396                 break;
34397         }
34398     }
34399 };
34400
34401 /**
34402  * Orientation constant - Create a vertical SplitBar
34403  * @static
34404  * @type Number
34405  */
34406 Roo.bootstrap.SplitBar.VERTICAL = 1;
34407
34408 /**
34409  * Orientation constant - Create a horizontal SplitBar
34410  * @static
34411  * @type Number
34412  */
34413 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34414
34415 /**
34416  * Placement constant - The resizing element is to the left of the splitter element
34417  * @static
34418  * @type Number
34419  */
34420 Roo.bootstrap.SplitBar.LEFT = 1;
34421
34422 /**
34423  * Placement constant - The resizing element is to the right of the splitter element
34424  * @static
34425  * @type Number
34426  */
34427 Roo.bootstrap.SplitBar.RIGHT = 2;
34428
34429 /**
34430  * Placement constant - The resizing element is positioned above the splitter element
34431  * @static
34432  * @type Number
34433  */
34434 Roo.bootstrap.SplitBar.TOP = 3;
34435
34436 /**
34437  * Placement constant - The resizing element is positioned under splitter element
34438  * @static
34439  * @type Number
34440  */
34441 Roo.bootstrap.SplitBar.BOTTOM = 4;
34442 Roo.namespace("Roo.bootstrap.layout");/*
34443  * Based on:
34444  * Ext JS Library 1.1.1
34445  * Copyright(c) 2006-2007, Ext JS, LLC.
34446  *
34447  * Originally Released Under LGPL - original licence link has changed is not relivant.
34448  *
34449  * Fork - LGPL
34450  * <script type="text/javascript">
34451  */
34452
34453 /**
34454  * @class Roo.bootstrap.layout.Manager
34455  * @extends Roo.bootstrap.Component
34456  * Base class for layout managers.
34457  */
34458 Roo.bootstrap.layout.Manager = function(config)
34459 {
34460     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34461
34462
34463
34464
34465
34466     /** false to disable window resize monitoring @type Boolean */
34467     this.monitorWindowResize = true;
34468     this.regions = {};
34469     this.addEvents({
34470         /**
34471          * @event layout
34472          * Fires when a layout is performed.
34473          * @param {Roo.LayoutManager} this
34474          */
34475         "layout" : true,
34476         /**
34477          * @event regionresized
34478          * Fires when the user resizes a region.
34479          * @param {Roo.LayoutRegion} region The resized region
34480          * @param {Number} newSize The new size (width for east/west, height for north/south)
34481          */
34482         "regionresized" : true,
34483         /**
34484          * @event regioncollapsed
34485          * Fires when a region is collapsed.
34486          * @param {Roo.LayoutRegion} region The collapsed region
34487          */
34488         "regioncollapsed" : true,
34489         /**
34490          * @event regionexpanded
34491          * Fires when a region is expanded.
34492          * @param {Roo.LayoutRegion} region The expanded region
34493          */
34494         "regionexpanded" : true
34495     });
34496     this.updating = false;
34497
34498     if (config.el) {
34499         this.el = Roo.get(config.el);
34500         this.initEvents();
34501     }
34502
34503 };
34504
34505 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34506
34507
34508     regions : null,
34509
34510     monitorWindowResize : true,
34511
34512
34513     updating : false,
34514
34515
34516     onRender : function(ct, position)
34517     {
34518         if(!this.el){
34519             this.el = Roo.get(ct);
34520             this.initEvents();
34521         }
34522         //this.fireEvent('render',this);
34523     },
34524
34525
34526     initEvents: function()
34527     {
34528
34529
34530         // ie scrollbar fix
34531         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34532             document.body.scroll = "no";
34533         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34534             this.el.position('relative');
34535         }
34536         this.id = this.el.id;
34537         this.el.addClass("roo-layout-container");
34538         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34539         if(this.el.dom != document.body ) {
34540             this.el.on('resize', this.layout,this);
34541             this.el.on('show', this.layout,this);
34542         }
34543
34544     },
34545
34546     /**
34547      * Returns true if this layout is currently being updated
34548      * @return {Boolean}
34549      */
34550     isUpdating : function(){
34551         return this.updating;
34552     },
34553
34554     /**
34555      * Suspend the LayoutManager from doing auto-layouts while
34556      * making multiple add or remove calls
34557      */
34558     beginUpdate : function(){
34559         this.updating = true;
34560     },
34561
34562     /**
34563      * Restore auto-layouts and optionally disable the manager from performing a layout
34564      * @param {Boolean} noLayout true to disable a layout update
34565      */
34566     endUpdate : function(noLayout){
34567         this.updating = false;
34568         if(!noLayout){
34569             this.layout();
34570         }
34571     },
34572
34573     layout: function(){
34574         // abstract...
34575     },
34576
34577     onRegionResized : function(region, newSize){
34578         this.fireEvent("regionresized", region, newSize);
34579         this.layout();
34580     },
34581
34582     onRegionCollapsed : function(region){
34583         this.fireEvent("regioncollapsed", region);
34584     },
34585
34586     onRegionExpanded : function(region){
34587         this.fireEvent("regionexpanded", region);
34588     },
34589
34590     /**
34591      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34592      * performs box-model adjustments.
34593      * @return {Object} The size as an object {width: (the width), height: (the height)}
34594      */
34595     getViewSize : function()
34596     {
34597         var size;
34598         if(this.el.dom != document.body){
34599             size = this.el.getSize();
34600         }else{
34601             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34602         }
34603         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34604         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34605         return size;
34606     },
34607
34608     /**
34609      * Returns the Element this layout is bound to.
34610      * @return {Roo.Element}
34611      */
34612     getEl : function(){
34613         return this.el;
34614     },
34615
34616     /**
34617      * Returns the specified region.
34618      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34619      * @return {Roo.LayoutRegion}
34620      */
34621     getRegion : function(target){
34622         return this.regions[target.toLowerCase()];
34623     },
34624
34625     onWindowResize : function(){
34626         if(this.monitorWindowResize){
34627             this.layout();
34628         }
34629     }
34630 });
34631 /*
34632  * Based on:
34633  * Ext JS Library 1.1.1
34634  * Copyright(c) 2006-2007, Ext JS, LLC.
34635  *
34636  * Originally Released Under LGPL - original licence link has changed is not relivant.
34637  *
34638  * Fork - LGPL
34639  * <script type="text/javascript">
34640  */
34641 /**
34642  * @class Roo.bootstrap.layout.Border
34643  * @extends Roo.bootstrap.layout.Manager
34644  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34645  * please see: examples/bootstrap/nested.html<br><br>
34646  
34647 <b>The container the layout is rendered into can be either the body element or any other element.
34648 If it is not the body element, the container needs to either be an absolute positioned element,
34649 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34650 the container size if it is not the body element.</b>
34651
34652 * @constructor
34653 * Create a new Border
34654 * @param {Object} config Configuration options
34655  */
34656 Roo.bootstrap.layout.Border = function(config){
34657     config = config || {};
34658     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34659     
34660     
34661     
34662     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34663         if(config[region]){
34664             config[region].region = region;
34665             this.addRegion(config[region]);
34666         }
34667     },this);
34668     
34669 };
34670
34671 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34672
34673 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34674     /**
34675      * Creates and adds a new region if it doesn't already exist.
34676      * @param {String} target The target region key (north, south, east, west or center).
34677      * @param {Object} config The regions config object
34678      * @return {BorderLayoutRegion} The new region
34679      */
34680     addRegion : function(config)
34681     {
34682         if(!this.regions[config.region]){
34683             var r = this.factory(config);
34684             this.bindRegion(r);
34685         }
34686         return this.regions[config.region];
34687     },
34688
34689     // private (kinda)
34690     bindRegion : function(r){
34691         this.regions[r.config.region] = r;
34692         
34693         r.on("visibilitychange",    this.layout, this);
34694         r.on("paneladded",          this.layout, this);
34695         r.on("panelremoved",        this.layout, this);
34696         r.on("invalidated",         this.layout, this);
34697         r.on("resized",             this.onRegionResized, this);
34698         r.on("collapsed",           this.onRegionCollapsed, this);
34699         r.on("expanded",            this.onRegionExpanded, this);
34700     },
34701
34702     /**
34703      * Performs a layout update.
34704      */
34705     layout : function()
34706     {
34707         if(this.updating) {
34708             return;
34709         }
34710         
34711         // render all the rebions if they have not been done alreayd?
34712         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34713             if(this.regions[region] && !this.regions[region].bodyEl){
34714                 this.regions[region].onRender(this.el)
34715             }
34716         },this);
34717         
34718         var size = this.getViewSize();
34719         var w = size.width;
34720         var h = size.height;
34721         var centerW = w;
34722         var centerH = h;
34723         var centerY = 0;
34724         var centerX = 0;
34725         //var x = 0, y = 0;
34726
34727         var rs = this.regions;
34728         var north = rs["north"];
34729         var south = rs["south"]; 
34730         var west = rs["west"];
34731         var east = rs["east"];
34732         var center = rs["center"];
34733         //if(this.hideOnLayout){ // not supported anymore
34734             //c.el.setStyle("display", "none");
34735         //}
34736         if(north && north.isVisible()){
34737             var b = north.getBox();
34738             var m = north.getMargins();
34739             b.width = w - (m.left+m.right);
34740             b.x = m.left;
34741             b.y = m.top;
34742             centerY = b.height + b.y + m.bottom;
34743             centerH -= centerY;
34744             north.updateBox(this.safeBox(b));
34745         }
34746         if(south && south.isVisible()){
34747             var b = south.getBox();
34748             var m = south.getMargins();
34749             b.width = w - (m.left+m.right);
34750             b.x = m.left;
34751             var totalHeight = (b.height + m.top + m.bottom);
34752             b.y = h - totalHeight + m.top;
34753             centerH -= totalHeight;
34754             south.updateBox(this.safeBox(b));
34755         }
34756         if(west && west.isVisible()){
34757             var b = west.getBox();
34758             var m = west.getMargins();
34759             b.height = centerH - (m.top+m.bottom);
34760             b.x = m.left;
34761             b.y = centerY + m.top;
34762             var totalWidth = (b.width + m.left + m.right);
34763             centerX += totalWidth;
34764             centerW -= totalWidth;
34765             west.updateBox(this.safeBox(b));
34766         }
34767         if(east && east.isVisible()){
34768             var b = east.getBox();
34769             var m = east.getMargins();
34770             b.height = centerH - (m.top+m.bottom);
34771             var totalWidth = (b.width + m.left + m.right);
34772             b.x = w - totalWidth + m.left;
34773             b.y = centerY + m.top;
34774             centerW -= totalWidth;
34775             east.updateBox(this.safeBox(b));
34776         }
34777         if(center){
34778             var m = center.getMargins();
34779             var centerBox = {
34780                 x: centerX + m.left,
34781                 y: centerY + m.top,
34782                 width: centerW - (m.left+m.right),
34783                 height: centerH - (m.top+m.bottom)
34784             };
34785             //if(this.hideOnLayout){
34786                 //center.el.setStyle("display", "block");
34787             //}
34788             center.updateBox(this.safeBox(centerBox));
34789         }
34790         this.el.repaint();
34791         this.fireEvent("layout", this);
34792     },
34793
34794     // private
34795     safeBox : function(box){
34796         box.width = Math.max(0, box.width);
34797         box.height = Math.max(0, box.height);
34798         return box;
34799     },
34800
34801     /**
34802      * Adds a ContentPanel (or subclass) to this layout.
34803      * @param {String} target The target region key (north, south, east, west or center).
34804      * @param {Roo.ContentPanel} panel The panel to add
34805      * @return {Roo.ContentPanel} The added panel
34806      */
34807     add : function(target, panel){
34808          
34809         target = target.toLowerCase();
34810         return this.regions[target].add(panel);
34811     },
34812
34813     /**
34814      * Remove a ContentPanel (or subclass) to this layout.
34815      * @param {String} target The target region key (north, south, east, west or center).
34816      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34817      * @return {Roo.ContentPanel} The removed panel
34818      */
34819     remove : function(target, panel){
34820         target = target.toLowerCase();
34821         return this.regions[target].remove(panel);
34822     },
34823
34824     /**
34825      * Searches all regions for a panel with the specified id
34826      * @param {String} panelId
34827      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34828      */
34829     findPanel : function(panelId){
34830         var rs = this.regions;
34831         for(var target in rs){
34832             if(typeof rs[target] != "function"){
34833                 var p = rs[target].getPanel(panelId);
34834                 if(p){
34835                     return p;
34836                 }
34837             }
34838         }
34839         return null;
34840     },
34841
34842     /**
34843      * Searches all regions for a panel with the specified id and activates (shows) it.
34844      * @param {String/ContentPanel} panelId The panels id or the panel itself
34845      * @return {Roo.ContentPanel} The shown panel or null
34846      */
34847     showPanel : function(panelId) {
34848       var rs = this.regions;
34849       for(var target in rs){
34850          var r = rs[target];
34851          if(typeof r != "function"){
34852             if(r.hasPanel(panelId)){
34853                return r.showPanel(panelId);
34854             }
34855          }
34856       }
34857       return null;
34858    },
34859
34860    /**
34861      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34862      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34863      */
34864    /*
34865     restoreState : function(provider){
34866         if(!provider){
34867             provider = Roo.state.Manager;
34868         }
34869         var sm = new Roo.LayoutStateManager();
34870         sm.init(this, provider);
34871     },
34872 */
34873  
34874  
34875     /**
34876      * Adds a xtype elements to the layout.
34877      * <pre><code>
34878
34879 layout.addxtype({
34880        xtype : 'ContentPanel',
34881        region: 'west',
34882        items: [ .... ]
34883    }
34884 );
34885
34886 layout.addxtype({
34887         xtype : 'NestedLayoutPanel',
34888         region: 'west',
34889         layout: {
34890            center: { },
34891            west: { }   
34892         },
34893         items : [ ... list of content panels or nested layout panels.. ]
34894    }
34895 );
34896 </code></pre>
34897      * @param {Object} cfg Xtype definition of item to add.
34898      */
34899     addxtype : function(cfg)
34900     {
34901         // basically accepts a pannel...
34902         // can accept a layout region..!?!?
34903         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34904         
34905         
34906         // theory?  children can only be panels??
34907         
34908         //if (!cfg.xtype.match(/Panel$/)) {
34909         //    return false;
34910         //}
34911         var ret = false;
34912         
34913         if (typeof(cfg.region) == 'undefined') {
34914             Roo.log("Failed to add Panel, region was not set");
34915             Roo.log(cfg);
34916             return false;
34917         }
34918         var region = cfg.region;
34919         delete cfg.region;
34920         
34921           
34922         var xitems = [];
34923         if (cfg.items) {
34924             xitems = cfg.items;
34925             delete cfg.items;
34926         }
34927         var nb = false;
34928         
34929         switch(cfg.xtype) 
34930         {
34931             case 'Content':  // ContentPanel (el, cfg)
34932             case 'Scroll':  // ContentPanel (el, cfg)
34933             case 'View': 
34934                 cfg.autoCreate = true;
34935                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34936                 //} else {
34937                 //    var el = this.el.createChild();
34938                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34939                 //}
34940                 
34941                 this.add(region, ret);
34942                 break;
34943             
34944             /*
34945             case 'TreePanel': // our new panel!
34946                 cfg.el = this.el.createChild();
34947                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34948                 this.add(region, ret);
34949                 break;
34950             */
34951             
34952             case 'Nest': 
34953                 // create a new Layout (which is  a Border Layout...
34954                 
34955                 var clayout = cfg.layout;
34956                 clayout.el  = this.el.createChild();
34957                 clayout.items   = clayout.items  || [];
34958                 
34959                 delete cfg.layout;
34960                 
34961                 // replace this exitems with the clayout ones..
34962                 xitems = clayout.items;
34963                  
34964                 // force background off if it's in center...
34965                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34966                     cfg.background = false;
34967                 }
34968                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34969                 
34970                 
34971                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34972                 //console.log('adding nested layout panel '  + cfg.toSource());
34973                 this.add(region, ret);
34974                 nb = {}; /// find first...
34975                 break;
34976             
34977             case 'Grid':
34978                 
34979                 // needs grid and region
34980                 
34981                 //var el = this.getRegion(region).el.createChild();
34982                 /*
34983                  *var el = this.el.createChild();
34984                 // create the grid first...
34985                 cfg.grid.container = el;
34986                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34987                 */
34988                 
34989                 if (region == 'center' && this.active ) {
34990                     cfg.background = false;
34991                 }
34992                 
34993                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34994                 
34995                 this.add(region, ret);
34996                 /*
34997                 if (cfg.background) {
34998                     // render grid on panel activation (if panel background)
34999                     ret.on('activate', function(gp) {
35000                         if (!gp.grid.rendered) {
35001                     //        gp.grid.render(el);
35002                         }
35003                     });
35004                 } else {
35005                   //  cfg.grid.render(el);
35006                 }
35007                 */
35008                 break;
35009            
35010            
35011             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35012                 // it was the old xcomponent building that caused this before.
35013                 // espeically if border is the top element in the tree.
35014                 ret = this;
35015                 break; 
35016                 
35017                     
35018                 
35019                 
35020                 
35021             default:
35022                 /*
35023                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35024                     
35025                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35026                     this.add(region, ret);
35027                 } else {
35028                 */
35029                     Roo.log(cfg);
35030                     throw "Can not add '" + cfg.xtype + "' to Border";
35031                     return null;
35032              
35033                                 
35034              
35035         }
35036         this.beginUpdate();
35037         // add children..
35038         var region = '';
35039         var abn = {};
35040         Roo.each(xitems, function(i)  {
35041             region = nb && i.region ? i.region : false;
35042             
35043             var add = ret.addxtype(i);
35044            
35045             if (region) {
35046                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35047                 if (!i.background) {
35048                     abn[region] = nb[region] ;
35049                 }
35050             }
35051             
35052         });
35053         this.endUpdate();
35054
35055         // make the last non-background panel active..
35056         //if (nb) { Roo.log(abn); }
35057         if (nb) {
35058             
35059             for(var r in abn) {
35060                 region = this.getRegion(r);
35061                 if (region) {
35062                     // tried using nb[r], but it does not work..
35063                      
35064                     region.showPanel(abn[r]);
35065                    
35066                 }
35067             }
35068         }
35069         return ret;
35070         
35071     },
35072     
35073     
35074 // private
35075     factory : function(cfg)
35076     {
35077         
35078         var validRegions = Roo.bootstrap.layout.Border.regions;
35079
35080         var target = cfg.region;
35081         cfg.mgr = this;
35082         
35083         var r = Roo.bootstrap.layout;
35084         Roo.log(target);
35085         switch(target){
35086             case "north":
35087                 return new r.North(cfg);
35088             case "south":
35089                 return new r.South(cfg);
35090             case "east":
35091                 return new r.East(cfg);
35092             case "west":
35093                 return new r.West(cfg);
35094             case "center":
35095                 return new r.Center(cfg);
35096         }
35097         throw 'Layout region "'+target+'" not supported.';
35098     }
35099     
35100     
35101 });
35102  /*
35103  * Based on:
35104  * Ext JS Library 1.1.1
35105  * Copyright(c) 2006-2007, Ext JS, LLC.
35106  *
35107  * Originally Released Under LGPL - original licence link has changed is not relivant.
35108  *
35109  * Fork - LGPL
35110  * <script type="text/javascript">
35111  */
35112  
35113 /**
35114  * @class Roo.bootstrap.layout.Basic
35115  * @extends Roo.util.Observable
35116  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35117  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35118  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35119  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35120  * @cfg {string}   region  the region that it inhabits..
35121  * @cfg {bool}   skipConfig skip config?
35122  * 
35123
35124  */
35125 Roo.bootstrap.layout.Basic = function(config){
35126     
35127     this.mgr = config.mgr;
35128     
35129     this.position = config.region;
35130     
35131     var skipConfig = config.skipConfig;
35132     
35133     this.events = {
35134         /**
35135          * @scope Roo.BasicLayoutRegion
35136          */
35137         
35138         /**
35139          * @event beforeremove
35140          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35141          * @param {Roo.LayoutRegion} this
35142          * @param {Roo.ContentPanel} panel The panel
35143          * @param {Object} e The cancel event object
35144          */
35145         "beforeremove" : true,
35146         /**
35147          * @event invalidated
35148          * Fires when the layout for this region is changed.
35149          * @param {Roo.LayoutRegion} this
35150          */
35151         "invalidated" : true,
35152         /**
35153          * @event visibilitychange
35154          * Fires when this region is shown or hidden 
35155          * @param {Roo.LayoutRegion} this
35156          * @param {Boolean} visibility true or false
35157          */
35158         "visibilitychange" : true,
35159         /**
35160          * @event paneladded
35161          * Fires when a panel is added. 
35162          * @param {Roo.LayoutRegion} this
35163          * @param {Roo.ContentPanel} panel The panel
35164          */
35165         "paneladded" : true,
35166         /**
35167          * @event panelremoved
35168          * Fires when a panel is removed. 
35169          * @param {Roo.LayoutRegion} this
35170          * @param {Roo.ContentPanel} panel The panel
35171          */
35172         "panelremoved" : true,
35173         /**
35174          * @event beforecollapse
35175          * Fires when this region before collapse.
35176          * @param {Roo.LayoutRegion} this
35177          */
35178         "beforecollapse" : true,
35179         /**
35180          * @event collapsed
35181          * Fires when this region is collapsed.
35182          * @param {Roo.LayoutRegion} this
35183          */
35184         "collapsed" : true,
35185         /**
35186          * @event expanded
35187          * Fires when this region is expanded.
35188          * @param {Roo.LayoutRegion} this
35189          */
35190         "expanded" : true,
35191         /**
35192          * @event slideshow
35193          * Fires when this region is slid into view.
35194          * @param {Roo.LayoutRegion} this
35195          */
35196         "slideshow" : true,
35197         /**
35198          * @event slidehide
35199          * Fires when this region slides out of view. 
35200          * @param {Roo.LayoutRegion} this
35201          */
35202         "slidehide" : true,
35203         /**
35204          * @event panelactivated
35205          * Fires when a panel is activated. 
35206          * @param {Roo.LayoutRegion} this
35207          * @param {Roo.ContentPanel} panel The activated panel
35208          */
35209         "panelactivated" : true,
35210         /**
35211          * @event resized
35212          * Fires when the user resizes this region. 
35213          * @param {Roo.LayoutRegion} this
35214          * @param {Number} newSize The new size (width for east/west, height for north/south)
35215          */
35216         "resized" : true
35217     };
35218     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35219     this.panels = new Roo.util.MixedCollection();
35220     this.panels.getKey = this.getPanelId.createDelegate(this);
35221     this.box = null;
35222     this.activePanel = null;
35223     // ensure listeners are added...
35224     
35225     if (config.listeners || config.events) {
35226         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35227             listeners : config.listeners || {},
35228             events : config.events || {}
35229         });
35230     }
35231     
35232     if(skipConfig !== true){
35233         this.applyConfig(config);
35234     }
35235 };
35236
35237 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35238 {
35239     getPanelId : function(p){
35240         return p.getId();
35241     },
35242     
35243     applyConfig : function(config){
35244         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35245         this.config = config;
35246         
35247     },
35248     
35249     /**
35250      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35251      * the width, for horizontal (north, south) the height.
35252      * @param {Number} newSize The new width or height
35253      */
35254     resizeTo : function(newSize){
35255         var el = this.el ? this.el :
35256                  (this.activePanel ? this.activePanel.getEl() : null);
35257         if(el){
35258             switch(this.position){
35259                 case "east":
35260                 case "west":
35261                     el.setWidth(newSize);
35262                     this.fireEvent("resized", this, newSize);
35263                 break;
35264                 case "north":
35265                 case "south":
35266                     el.setHeight(newSize);
35267                     this.fireEvent("resized", this, newSize);
35268                 break;                
35269             }
35270         }
35271     },
35272     
35273     getBox : function(){
35274         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35275     },
35276     
35277     getMargins : function(){
35278         return this.margins;
35279     },
35280     
35281     updateBox : function(box){
35282         this.box = box;
35283         var el = this.activePanel.getEl();
35284         el.dom.style.left = box.x + "px";
35285         el.dom.style.top = box.y + "px";
35286         this.activePanel.setSize(box.width, box.height);
35287     },
35288     
35289     /**
35290      * Returns the container element for this region.
35291      * @return {Roo.Element}
35292      */
35293     getEl : function(){
35294         return this.activePanel;
35295     },
35296     
35297     /**
35298      * Returns true if this region is currently visible.
35299      * @return {Boolean}
35300      */
35301     isVisible : function(){
35302         return this.activePanel ? true : false;
35303     },
35304     
35305     setActivePanel : function(panel){
35306         panel = this.getPanel(panel);
35307         if(this.activePanel && this.activePanel != panel){
35308             this.activePanel.setActiveState(false);
35309             this.activePanel.getEl().setLeftTop(-10000,-10000);
35310         }
35311         this.activePanel = panel;
35312         panel.setActiveState(true);
35313         if(this.box){
35314             panel.setSize(this.box.width, this.box.height);
35315         }
35316         this.fireEvent("panelactivated", this, panel);
35317         this.fireEvent("invalidated");
35318     },
35319     
35320     /**
35321      * Show the specified panel.
35322      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35323      * @return {Roo.ContentPanel} The shown panel or null
35324      */
35325     showPanel : function(panel){
35326         panel = this.getPanel(panel);
35327         if(panel){
35328             this.setActivePanel(panel);
35329         }
35330         return panel;
35331     },
35332     
35333     /**
35334      * Get the active panel for this region.
35335      * @return {Roo.ContentPanel} The active panel or null
35336      */
35337     getActivePanel : function(){
35338         return this.activePanel;
35339     },
35340     
35341     /**
35342      * Add the passed ContentPanel(s)
35343      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35344      * @return {Roo.ContentPanel} The panel added (if only one was added)
35345      */
35346     add : function(panel){
35347         if(arguments.length > 1){
35348             for(var i = 0, len = arguments.length; i < len; i++) {
35349                 this.add(arguments[i]);
35350             }
35351             return null;
35352         }
35353         if(this.hasPanel(panel)){
35354             this.showPanel(panel);
35355             return panel;
35356         }
35357         var el = panel.getEl();
35358         if(el.dom.parentNode != this.mgr.el.dom){
35359             this.mgr.el.dom.appendChild(el.dom);
35360         }
35361         if(panel.setRegion){
35362             panel.setRegion(this);
35363         }
35364         this.panels.add(panel);
35365         el.setStyle("position", "absolute");
35366         if(!panel.background){
35367             this.setActivePanel(panel);
35368             if(this.config.initialSize && this.panels.getCount()==1){
35369                 this.resizeTo(this.config.initialSize);
35370             }
35371         }
35372         this.fireEvent("paneladded", this, panel);
35373         return panel;
35374     },
35375     
35376     /**
35377      * Returns true if the panel is in this region.
35378      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35379      * @return {Boolean}
35380      */
35381     hasPanel : function(panel){
35382         if(typeof panel == "object"){ // must be panel obj
35383             panel = panel.getId();
35384         }
35385         return this.getPanel(panel) ? true : false;
35386     },
35387     
35388     /**
35389      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35390      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35391      * @param {Boolean} preservePanel Overrides the config preservePanel option
35392      * @return {Roo.ContentPanel} The panel that was removed
35393      */
35394     remove : function(panel, preservePanel){
35395         panel = this.getPanel(panel);
35396         if(!panel){
35397             return null;
35398         }
35399         var e = {};
35400         this.fireEvent("beforeremove", this, panel, e);
35401         if(e.cancel === true){
35402             return null;
35403         }
35404         var panelId = panel.getId();
35405         this.panels.removeKey(panelId);
35406         return panel;
35407     },
35408     
35409     /**
35410      * Returns the panel specified or null if it's not in this region.
35411      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35412      * @return {Roo.ContentPanel}
35413      */
35414     getPanel : function(id){
35415         if(typeof id == "object"){ // must be panel obj
35416             return id;
35417         }
35418         return this.panels.get(id);
35419     },
35420     
35421     /**
35422      * Returns this regions position (north/south/east/west/center).
35423      * @return {String} 
35424      */
35425     getPosition: function(){
35426         return this.position;    
35427     }
35428 });/*
35429  * Based on:
35430  * Ext JS Library 1.1.1
35431  * Copyright(c) 2006-2007, Ext JS, LLC.
35432  *
35433  * Originally Released Under LGPL - original licence link has changed is not relivant.
35434  *
35435  * Fork - LGPL
35436  * <script type="text/javascript">
35437  */
35438  
35439 /**
35440  * @class Roo.bootstrap.layout.Region
35441  * @extends Roo.bootstrap.layout.Basic
35442  * This class represents a region in a layout manager.
35443  
35444  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35445  * @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})
35446  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35447  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35448  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35449  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35450  * @cfg {String}    title           The title for the region (overrides panel titles)
35451  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35452  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35453  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35454  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35455  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35456  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35457  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35458  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35459  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35460  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35461
35462  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35463  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35464  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35465  * @cfg {Number}    width           For East/West panels
35466  * @cfg {Number}    height          For North/South panels
35467  * @cfg {Boolean}   split           To show the splitter
35468  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35469  * 
35470  * @cfg {string}   cls             Extra CSS classes to add to region
35471  * 
35472  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35473  * @cfg {string}   region  the region that it inhabits..
35474  *
35475
35476  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35477  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35478
35479  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35480  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35481  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35482  */
35483 Roo.bootstrap.layout.Region = function(config)
35484 {
35485     this.applyConfig(config);
35486
35487     var mgr = config.mgr;
35488     var pos = config.region;
35489     config.skipConfig = true;
35490     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35491     
35492     if (mgr.el) {
35493         this.onRender(mgr.el);   
35494     }
35495      
35496     this.visible = true;
35497     this.collapsed = false;
35498     this.unrendered_panels = [];
35499 };
35500
35501 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35502
35503     position: '', // set by wrapper (eg. north/south etc..)
35504     unrendered_panels : null,  // unrendered panels.
35505     createBody : function(){
35506         /** This region's body element 
35507         * @type Roo.Element */
35508         this.bodyEl = this.el.createChild({
35509                 tag: "div",
35510                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35511         });
35512     },
35513
35514     onRender: function(ctr, pos)
35515     {
35516         var dh = Roo.DomHelper;
35517         /** This region's container element 
35518         * @type Roo.Element */
35519         this.el = dh.append(ctr.dom, {
35520                 tag: "div",
35521                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35522             }, true);
35523         /** This region's title element 
35524         * @type Roo.Element */
35525     
35526         this.titleEl = dh.append(this.el.dom,
35527             {
35528                     tag: "div",
35529                     unselectable: "on",
35530                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35531                     children:[
35532                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35533                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35534                     ]}, true);
35535         
35536         this.titleEl.enableDisplayMode();
35537         /** This region's title text element 
35538         * @type HTMLElement */
35539         this.titleTextEl = this.titleEl.dom.firstChild;
35540         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35541         /*
35542         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35543         this.closeBtn.enableDisplayMode();
35544         this.closeBtn.on("click", this.closeClicked, this);
35545         this.closeBtn.hide();
35546     */
35547         this.createBody(this.config);
35548         if(this.config.hideWhenEmpty){
35549             this.hide();
35550             this.on("paneladded", this.validateVisibility, this);
35551             this.on("panelremoved", this.validateVisibility, this);
35552         }
35553         if(this.autoScroll){
35554             this.bodyEl.setStyle("overflow", "auto");
35555         }else{
35556             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35557         }
35558         //if(c.titlebar !== false){
35559             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35560                 this.titleEl.hide();
35561             }else{
35562                 this.titleEl.show();
35563                 if(this.config.title){
35564                     this.titleTextEl.innerHTML = this.config.title;
35565                 }
35566             }
35567         //}
35568         if(this.config.collapsed){
35569             this.collapse(true);
35570         }
35571         if(this.config.hidden){
35572             this.hide();
35573         }
35574         
35575         if (this.unrendered_panels && this.unrendered_panels.length) {
35576             for (var i =0;i< this.unrendered_panels.length; i++) {
35577                 this.add(this.unrendered_panels[i]);
35578             }
35579             this.unrendered_panels = null;
35580             
35581         }
35582         
35583     },
35584     
35585     applyConfig : function(c)
35586     {
35587         /*
35588          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35589             var dh = Roo.DomHelper;
35590             if(c.titlebar !== false){
35591                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35592                 this.collapseBtn.on("click", this.collapse, this);
35593                 this.collapseBtn.enableDisplayMode();
35594                 /*
35595                 if(c.showPin === true || this.showPin){
35596                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35597                     this.stickBtn.enableDisplayMode();
35598                     this.stickBtn.on("click", this.expand, this);
35599                     this.stickBtn.hide();
35600                 }
35601                 
35602             }
35603             */
35604             /** This region's collapsed element
35605             * @type Roo.Element */
35606             /*
35607              *
35608             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35609                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35610             ]}, true);
35611             
35612             if(c.floatable !== false){
35613                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35614                this.collapsedEl.on("click", this.collapseClick, this);
35615             }
35616
35617             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35618                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35619                    id: "message", unselectable: "on", style:{"float":"left"}});
35620                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35621              }
35622             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35623             this.expandBtn.on("click", this.expand, this);
35624             
35625         }
35626         
35627         if(this.collapseBtn){
35628             this.collapseBtn.setVisible(c.collapsible == true);
35629         }
35630         
35631         this.cmargins = c.cmargins || this.cmargins ||
35632                          (this.position == "west" || this.position == "east" ?
35633                              {top: 0, left: 2, right:2, bottom: 0} :
35634                              {top: 2, left: 0, right:0, bottom: 2});
35635         */
35636         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35637         
35638         
35639         this.bottomTabs = c.tabPosition != "top";
35640         
35641         this.autoScroll = c.autoScroll || false;
35642         
35643         
35644        
35645         
35646         this.duration = c.duration || .30;
35647         this.slideDuration = c.slideDuration || .45;
35648         this.config = c;
35649        
35650     },
35651     /**
35652      * Returns true if this region is currently visible.
35653      * @return {Boolean}
35654      */
35655     isVisible : function(){
35656         return this.visible;
35657     },
35658
35659     /**
35660      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35661      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35662      */
35663     //setCollapsedTitle : function(title){
35664     //    title = title || "&#160;";
35665      //   if(this.collapsedTitleTextEl){
35666       //      this.collapsedTitleTextEl.innerHTML = title;
35667        // }
35668     //},
35669
35670     getBox : function(){
35671         var b;
35672       //  if(!this.collapsed){
35673             b = this.el.getBox(false, true);
35674        // }else{
35675           //  b = this.collapsedEl.getBox(false, true);
35676         //}
35677         return b;
35678     },
35679
35680     getMargins : function(){
35681         return this.margins;
35682         //return this.collapsed ? this.cmargins : this.margins;
35683     },
35684 /*
35685     highlight : function(){
35686         this.el.addClass("x-layout-panel-dragover");
35687     },
35688
35689     unhighlight : function(){
35690         this.el.removeClass("x-layout-panel-dragover");
35691     },
35692 */
35693     updateBox : function(box)
35694     {
35695         if (!this.bodyEl) {
35696             return; // not rendered yet..
35697         }
35698         
35699         this.box = box;
35700         if(!this.collapsed){
35701             this.el.dom.style.left = box.x + "px";
35702             this.el.dom.style.top = box.y + "px";
35703             this.updateBody(box.width, box.height);
35704         }else{
35705             this.collapsedEl.dom.style.left = box.x + "px";
35706             this.collapsedEl.dom.style.top = box.y + "px";
35707             this.collapsedEl.setSize(box.width, box.height);
35708         }
35709         if(this.tabs){
35710             this.tabs.autoSizeTabs();
35711         }
35712     },
35713
35714     updateBody : function(w, h)
35715     {
35716         if(w !== null){
35717             this.el.setWidth(w);
35718             w -= this.el.getBorderWidth("rl");
35719             if(this.config.adjustments){
35720                 w += this.config.adjustments[0];
35721             }
35722         }
35723         if(h !== null && h > 0){
35724             this.el.setHeight(h);
35725             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35726             h -= this.el.getBorderWidth("tb");
35727             if(this.config.adjustments){
35728                 h += this.config.adjustments[1];
35729             }
35730             this.bodyEl.setHeight(h);
35731             if(this.tabs){
35732                 h = this.tabs.syncHeight(h);
35733             }
35734         }
35735         if(this.panelSize){
35736             w = w !== null ? w : this.panelSize.width;
35737             h = h !== null ? h : this.panelSize.height;
35738         }
35739         if(this.activePanel){
35740             var el = this.activePanel.getEl();
35741             w = w !== null ? w : el.getWidth();
35742             h = h !== null ? h : el.getHeight();
35743             this.panelSize = {width: w, height: h};
35744             this.activePanel.setSize(w, h);
35745         }
35746         if(Roo.isIE && this.tabs){
35747             this.tabs.el.repaint();
35748         }
35749     },
35750
35751     /**
35752      * Returns the container element for this region.
35753      * @return {Roo.Element}
35754      */
35755     getEl : function(){
35756         return this.el;
35757     },
35758
35759     /**
35760      * Hides this region.
35761      */
35762     hide : function(){
35763         //if(!this.collapsed){
35764             this.el.dom.style.left = "-2000px";
35765             this.el.hide();
35766         //}else{
35767          //   this.collapsedEl.dom.style.left = "-2000px";
35768          //   this.collapsedEl.hide();
35769        // }
35770         this.visible = false;
35771         this.fireEvent("visibilitychange", this, false);
35772     },
35773
35774     /**
35775      * Shows this region if it was previously hidden.
35776      */
35777     show : function(){
35778         //if(!this.collapsed){
35779             this.el.show();
35780         //}else{
35781         //    this.collapsedEl.show();
35782        // }
35783         this.visible = true;
35784         this.fireEvent("visibilitychange", this, true);
35785     },
35786 /*
35787     closeClicked : function(){
35788         if(this.activePanel){
35789             this.remove(this.activePanel);
35790         }
35791     },
35792
35793     collapseClick : function(e){
35794         if(this.isSlid){
35795            e.stopPropagation();
35796            this.slideIn();
35797         }else{
35798            e.stopPropagation();
35799            this.slideOut();
35800         }
35801     },
35802 */
35803     /**
35804      * Collapses this region.
35805      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35806      */
35807     /*
35808     collapse : function(skipAnim, skipCheck = false){
35809         if(this.collapsed) {
35810             return;
35811         }
35812         
35813         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35814             
35815             this.collapsed = true;
35816             if(this.split){
35817                 this.split.el.hide();
35818             }
35819             if(this.config.animate && skipAnim !== true){
35820                 this.fireEvent("invalidated", this);
35821                 this.animateCollapse();
35822             }else{
35823                 this.el.setLocation(-20000,-20000);
35824                 this.el.hide();
35825                 this.collapsedEl.show();
35826                 this.fireEvent("collapsed", this);
35827                 this.fireEvent("invalidated", this);
35828             }
35829         }
35830         
35831     },
35832 */
35833     animateCollapse : function(){
35834         // overridden
35835     },
35836
35837     /**
35838      * Expands this region if it was previously collapsed.
35839      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35840      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35841      */
35842     /*
35843     expand : function(e, skipAnim){
35844         if(e) {
35845             e.stopPropagation();
35846         }
35847         if(!this.collapsed || this.el.hasActiveFx()) {
35848             return;
35849         }
35850         if(this.isSlid){
35851             this.afterSlideIn();
35852             skipAnim = true;
35853         }
35854         this.collapsed = false;
35855         if(this.config.animate && skipAnim !== true){
35856             this.animateExpand();
35857         }else{
35858             this.el.show();
35859             if(this.split){
35860                 this.split.el.show();
35861             }
35862             this.collapsedEl.setLocation(-2000,-2000);
35863             this.collapsedEl.hide();
35864             this.fireEvent("invalidated", this);
35865             this.fireEvent("expanded", this);
35866         }
35867     },
35868 */
35869     animateExpand : function(){
35870         // overridden
35871     },
35872
35873     initTabs : function()
35874     {
35875         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35876         
35877         var ts = new Roo.bootstrap.panel.Tabs({
35878                 el: this.bodyEl.dom,
35879                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35880                 disableTooltips: this.config.disableTabTips,
35881                 toolbar : this.config.toolbar
35882             });
35883         
35884         if(this.config.hideTabs){
35885             ts.stripWrap.setDisplayed(false);
35886         }
35887         this.tabs = ts;
35888         ts.resizeTabs = this.config.resizeTabs === true;
35889         ts.minTabWidth = this.config.minTabWidth || 40;
35890         ts.maxTabWidth = this.config.maxTabWidth || 250;
35891         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35892         ts.monitorResize = false;
35893         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35894         ts.bodyEl.addClass('roo-layout-tabs-body');
35895         this.panels.each(this.initPanelAsTab, this);
35896     },
35897
35898     initPanelAsTab : function(panel){
35899         var ti = this.tabs.addTab(
35900             panel.getEl().id,
35901             panel.getTitle(),
35902             null,
35903             this.config.closeOnTab && panel.isClosable(),
35904             panel.tpl
35905         );
35906         if(panel.tabTip !== undefined){
35907             ti.setTooltip(panel.tabTip);
35908         }
35909         ti.on("activate", function(){
35910               this.setActivePanel(panel);
35911         }, this);
35912         
35913         if(this.config.closeOnTab){
35914             ti.on("beforeclose", function(t, e){
35915                 e.cancel = true;
35916                 this.remove(panel);
35917             }, this);
35918         }
35919         
35920         panel.tabItem = ti;
35921         
35922         return ti;
35923     },
35924
35925     updatePanelTitle : function(panel, title)
35926     {
35927         if(this.activePanel == panel){
35928             this.updateTitle(title);
35929         }
35930         if(this.tabs){
35931             var ti = this.tabs.getTab(panel.getEl().id);
35932             ti.setText(title);
35933             if(panel.tabTip !== undefined){
35934                 ti.setTooltip(panel.tabTip);
35935             }
35936         }
35937     },
35938
35939     updateTitle : function(title){
35940         if(this.titleTextEl && !this.config.title){
35941             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35942         }
35943     },
35944
35945     setActivePanel : function(panel)
35946     {
35947         panel = this.getPanel(panel);
35948         if(this.activePanel && this.activePanel != panel){
35949             if(this.activePanel.setActiveState(false) === false){
35950                 return;
35951             }
35952         }
35953         this.activePanel = panel;
35954         panel.setActiveState(true);
35955         if(this.panelSize){
35956             panel.setSize(this.panelSize.width, this.panelSize.height);
35957         }
35958         if(this.closeBtn){
35959             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35960         }
35961         this.updateTitle(panel.getTitle());
35962         if(this.tabs){
35963             this.fireEvent("invalidated", this);
35964         }
35965         this.fireEvent("panelactivated", this, panel);
35966     },
35967
35968     /**
35969      * Shows the specified panel.
35970      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35971      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35972      */
35973     showPanel : function(panel)
35974     {
35975         panel = this.getPanel(panel);
35976         if(panel){
35977             if(this.tabs){
35978                 var tab = this.tabs.getTab(panel.getEl().id);
35979                 if(tab.isHidden()){
35980                     this.tabs.unhideTab(tab.id);
35981                 }
35982                 tab.activate();
35983             }else{
35984                 this.setActivePanel(panel);
35985             }
35986         }
35987         return panel;
35988     },
35989
35990     /**
35991      * Get the active panel for this region.
35992      * @return {Roo.ContentPanel} The active panel or null
35993      */
35994     getActivePanel : function(){
35995         return this.activePanel;
35996     },
35997
35998     validateVisibility : function(){
35999         if(this.panels.getCount() < 1){
36000             this.updateTitle("&#160;");
36001             this.closeBtn.hide();
36002             this.hide();
36003         }else{
36004             if(!this.isVisible()){
36005                 this.show();
36006             }
36007         }
36008     },
36009
36010     /**
36011      * Adds the passed ContentPanel(s) to this region.
36012      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36013      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36014      */
36015     add : function(panel)
36016     {
36017         if(arguments.length > 1){
36018             for(var i = 0, len = arguments.length; i < len; i++) {
36019                 this.add(arguments[i]);
36020             }
36021             return null;
36022         }
36023         
36024         // if we have not been rendered yet, then we can not really do much of this..
36025         if (!this.bodyEl) {
36026             this.unrendered_panels.push(panel);
36027             return panel;
36028         }
36029         
36030         
36031         
36032         
36033         if(this.hasPanel(panel)){
36034             this.showPanel(panel);
36035             return panel;
36036         }
36037         panel.setRegion(this);
36038         this.panels.add(panel);
36039        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36040             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36041             // and hide them... ???
36042             this.bodyEl.dom.appendChild(panel.getEl().dom);
36043             if(panel.background !== true){
36044                 this.setActivePanel(panel);
36045             }
36046             this.fireEvent("paneladded", this, panel);
36047             return panel;
36048         }
36049         */
36050         if(!this.tabs){
36051             this.initTabs();
36052         }else{
36053             this.initPanelAsTab(panel);
36054         }
36055         
36056         
36057         if(panel.background !== true){
36058             this.tabs.activate(panel.getEl().id);
36059         }
36060         this.fireEvent("paneladded", this, panel);
36061         return panel;
36062     },
36063
36064     /**
36065      * Hides the tab for the specified panel.
36066      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36067      */
36068     hidePanel : function(panel){
36069         if(this.tabs && (panel = this.getPanel(panel))){
36070             this.tabs.hideTab(panel.getEl().id);
36071         }
36072     },
36073
36074     /**
36075      * Unhides the tab for a previously hidden panel.
36076      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36077      */
36078     unhidePanel : function(panel){
36079         if(this.tabs && (panel = this.getPanel(panel))){
36080             this.tabs.unhideTab(panel.getEl().id);
36081         }
36082     },
36083
36084     clearPanels : function(){
36085         while(this.panels.getCount() > 0){
36086              this.remove(this.panels.first());
36087         }
36088     },
36089
36090     /**
36091      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36092      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36093      * @param {Boolean} preservePanel Overrides the config preservePanel option
36094      * @return {Roo.ContentPanel} The panel that was removed
36095      */
36096     remove : function(panel, preservePanel)
36097     {
36098         panel = this.getPanel(panel);
36099         if(!panel){
36100             return null;
36101         }
36102         var e = {};
36103         this.fireEvent("beforeremove", this, panel, e);
36104         if(e.cancel === true){
36105             return null;
36106         }
36107         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36108         var panelId = panel.getId();
36109         this.panels.removeKey(panelId);
36110         if(preservePanel){
36111             document.body.appendChild(panel.getEl().dom);
36112         }
36113         if(this.tabs){
36114             this.tabs.removeTab(panel.getEl().id);
36115         }else if (!preservePanel){
36116             this.bodyEl.dom.removeChild(panel.getEl().dom);
36117         }
36118         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36119             var p = this.panels.first();
36120             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36121             tempEl.appendChild(p.getEl().dom);
36122             this.bodyEl.update("");
36123             this.bodyEl.dom.appendChild(p.getEl().dom);
36124             tempEl = null;
36125             this.updateTitle(p.getTitle());
36126             this.tabs = null;
36127             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36128             this.setActivePanel(p);
36129         }
36130         panel.setRegion(null);
36131         if(this.activePanel == panel){
36132             this.activePanel = null;
36133         }
36134         if(this.config.autoDestroy !== false && preservePanel !== true){
36135             try{panel.destroy();}catch(e){}
36136         }
36137         this.fireEvent("panelremoved", this, panel);
36138         return panel;
36139     },
36140
36141     /**
36142      * Returns the TabPanel component used by this region
36143      * @return {Roo.TabPanel}
36144      */
36145     getTabs : function(){
36146         return this.tabs;
36147     },
36148
36149     createTool : function(parentEl, className){
36150         var btn = Roo.DomHelper.append(parentEl, {
36151             tag: "div",
36152             cls: "x-layout-tools-button",
36153             children: [ {
36154                 tag: "div",
36155                 cls: "roo-layout-tools-button-inner " + className,
36156                 html: "&#160;"
36157             }]
36158         }, true);
36159         btn.addClassOnOver("roo-layout-tools-button-over");
36160         return btn;
36161     }
36162 });/*
36163  * Based on:
36164  * Ext JS Library 1.1.1
36165  * Copyright(c) 2006-2007, Ext JS, LLC.
36166  *
36167  * Originally Released Under LGPL - original licence link has changed is not relivant.
36168  *
36169  * Fork - LGPL
36170  * <script type="text/javascript">
36171  */
36172  
36173
36174
36175 /**
36176  * @class Roo.SplitLayoutRegion
36177  * @extends Roo.LayoutRegion
36178  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36179  */
36180 Roo.bootstrap.layout.Split = function(config){
36181     this.cursor = config.cursor;
36182     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36183 };
36184
36185 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36186 {
36187     splitTip : "Drag to resize.",
36188     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36189     useSplitTips : false,
36190
36191     applyConfig : function(config){
36192         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36193     },
36194     
36195     onRender : function(ctr,pos) {
36196         
36197         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36198         if(!this.config.split){
36199             return;
36200         }
36201         if(!this.split){
36202             
36203             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36204                             tag: "div",
36205                             id: this.el.id + "-split",
36206                             cls: "roo-layout-split roo-layout-split-"+this.position,
36207                             html: "&#160;"
36208             });
36209             /** The SplitBar for this region 
36210             * @type Roo.SplitBar */
36211             // does not exist yet...
36212             Roo.log([this.position, this.orientation]);
36213             
36214             this.split = new Roo.bootstrap.SplitBar({
36215                 dragElement : splitEl,
36216                 resizingElement: this.el,
36217                 orientation : this.orientation
36218             });
36219             
36220             this.split.on("moved", this.onSplitMove, this);
36221             this.split.useShim = this.config.useShim === true;
36222             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36223             if(this.useSplitTips){
36224                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36225             }
36226             //if(config.collapsible){
36227             //    this.split.el.on("dblclick", this.collapse,  this);
36228             //}
36229         }
36230         if(typeof this.config.minSize != "undefined"){
36231             this.split.minSize = this.config.minSize;
36232         }
36233         if(typeof this.config.maxSize != "undefined"){
36234             this.split.maxSize = this.config.maxSize;
36235         }
36236         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36237             this.hideSplitter();
36238         }
36239         
36240     },
36241
36242     getHMaxSize : function(){
36243          var cmax = this.config.maxSize || 10000;
36244          var center = this.mgr.getRegion("center");
36245          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36246     },
36247
36248     getVMaxSize : function(){
36249          var cmax = this.config.maxSize || 10000;
36250          var center = this.mgr.getRegion("center");
36251          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36252     },
36253
36254     onSplitMove : function(split, newSize){
36255         this.fireEvent("resized", this, newSize);
36256     },
36257     
36258     /** 
36259      * Returns the {@link Roo.SplitBar} for this region.
36260      * @return {Roo.SplitBar}
36261      */
36262     getSplitBar : function(){
36263         return this.split;
36264     },
36265     
36266     hide : function(){
36267         this.hideSplitter();
36268         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36269     },
36270
36271     hideSplitter : function(){
36272         if(this.split){
36273             this.split.el.setLocation(-2000,-2000);
36274             this.split.el.hide();
36275         }
36276     },
36277
36278     show : function(){
36279         if(this.split){
36280             this.split.el.show();
36281         }
36282         Roo.bootstrap.layout.Split.superclass.show.call(this);
36283     },
36284     
36285     beforeSlide: function(){
36286         if(Roo.isGecko){// firefox overflow auto bug workaround
36287             this.bodyEl.clip();
36288             if(this.tabs) {
36289                 this.tabs.bodyEl.clip();
36290             }
36291             if(this.activePanel){
36292                 this.activePanel.getEl().clip();
36293                 
36294                 if(this.activePanel.beforeSlide){
36295                     this.activePanel.beforeSlide();
36296                 }
36297             }
36298         }
36299     },
36300     
36301     afterSlide : function(){
36302         if(Roo.isGecko){// firefox overflow auto bug workaround
36303             this.bodyEl.unclip();
36304             if(this.tabs) {
36305                 this.tabs.bodyEl.unclip();
36306             }
36307             if(this.activePanel){
36308                 this.activePanel.getEl().unclip();
36309                 if(this.activePanel.afterSlide){
36310                     this.activePanel.afterSlide();
36311                 }
36312             }
36313         }
36314     },
36315
36316     initAutoHide : function(){
36317         if(this.autoHide !== false){
36318             if(!this.autoHideHd){
36319                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36320                 this.autoHideHd = {
36321                     "mouseout": function(e){
36322                         if(!e.within(this.el, true)){
36323                             st.delay(500);
36324                         }
36325                     },
36326                     "mouseover" : function(e){
36327                         st.cancel();
36328                     },
36329                     scope : this
36330                 };
36331             }
36332             this.el.on(this.autoHideHd);
36333         }
36334     },
36335
36336     clearAutoHide : function(){
36337         if(this.autoHide !== false){
36338             this.el.un("mouseout", this.autoHideHd.mouseout);
36339             this.el.un("mouseover", this.autoHideHd.mouseover);
36340         }
36341     },
36342
36343     clearMonitor : function(){
36344         Roo.get(document).un("click", this.slideInIf, this);
36345     },
36346
36347     // these names are backwards but not changed for compat
36348     slideOut : function(){
36349         if(this.isSlid || this.el.hasActiveFx()){
36350             return;
36351         }
36352         this.isSlid = true;
36353         if(this.collapseBtn){
36354             this.collapseBtn.hide();
36355         }
36356         this.closeBtnState = this.closeBtn.getStyle('display');
36357         this.closeBtn.hide();
36358         if(this.stickBtn){
36359             this.stickBtn.show();
36360         }
36361         this.el.show();
36362         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36363         this.beforeSlide();
36364         this.el.setStyle("z-index", 10001);
36365         this.el.slideIn(this.getSlideAnchor(), {
36366             callback: function(){
36367                 this.afterSlide();
36368                 this.initAutoHide();
36369                 Roo.get(document).on("click", this.slideInIf, this);
36370                 this.fireEvent("slideshow", this);
36371             },
36372             scope: this,
36373             block: true
36374         });
36375     },
36376
36377     afterSlideIn : function(){
36378         this.clearAutoHide();
36379         this.isSlid = false;
36380         this.clearMonitor();
36381         this.el.setStyle("z-index", "");
36382         if(this.collapseBtn){
36383             this.collapseBtn.show();
36384         }
36385         this.closeBtn.setStyle('display', this.closeBtnState);
36386         if(this.stickBtn){
36387             this.stickBtn.hide();
36388         }
36389         this.fireEvent("slidehide", this);
36390     },
36391
36392     slideIn : function(cb){
36393         if(!this.isSlid || this.el.hasActiveFx()){
36394             Roo.callback(cb);
36395             return;
36396         }
36397         this.isSlid = false;
36398         this.beforeSlide();
36399         this.el.slideOut(this.getSlideAnchor(), {
36400             callback: function(){
36401                 this.el.setLeftTop(-10000, -10000);
36402                 this.afterSlide();
36403                 this.afterSlideIn();
36404                 Roo.callback(cb);
36405             },
36406             scope: this,
36407             block: true
36408         });
36409     },
36410     
36411     slideInIf : function(e){
36412         if(!e.within(this.el)){
36413             this.slideIn();
36414         }
36415     },
36416
36417     animateCollapse : function(){
36418         this.beforeSlide();
36419         this.el.setStyle("z-index", 20000);
36420         var anchor = this.getSlideAnchor();
36421         this.el.slideOut(anchor, {
36422             callback : function(){
36423                 this.el.setStyle("z-index", "");
36424                 this.collapsedEl.slideIn(anchor, {duration:.3});
36425                 this.afterSlide();
36426                 this.el.setLocation(-10000,-10000);
36427                 this.el.hide();
36428                 this.fireEvent("collapsed", this);
36429             },
36430             scope: this,
36431             block: true
36432         });
36433     },
36434
36435     animateExpand : function(){
36436         this.beforeSlide();
36437         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36438         this.el.setStyle("z-index", 20000);
36439         this.collapsedEl.hide({
36440             duration:.1
36441         });
36442         this.el.slideIn(this.getSlideAnchor(), {
36443             callback : function(){
36444                 this.el.setStyle("z-index", "");
36445                 this.afterSlide();
36446                 if(this.split){
36447                     this.split.el.show();
36448                 }
36449                 this.fireEvent("invalidated", this);
36450                 this.fireEvent("expanded", this);
36451             },
36452             scope: this,
36453             block: true
36454         });
36455     },
36456
36457     anchors : {
36458         "west" : "left",
36459         "east" : "right",
36460         "north" : "top",
36461         "south" : "bottom"
36462     },
36463
36464     sanchors : {
36465         "west" : "l",
36466         "east" : "r",
36467         "north" : "t",
36468         "south" : "b"
36469     },
36470
36471     canchors : {
36472         "west" : "tl-tr",
36473         "east" : "tr-tl",
36474         "north" : "tl-bl",
36475         "south" : "bl-tl"
36476     },
36477
36478     getAnchor : function(){
36479         return this.anchors[this.position];
36480     },
36481
36482     getCollapseAnchor : function(){
36483         return this.canchors[this.position];
36484     },
36485
36486     getSlideAnchor : function(){
36487         return this.sanchors[this.position];
36488     },
36489
36490     getAlignAdj : function(){
36491         var cm = this.cmargins;
36492         switch(this.position){
36493             case "west":
36494                 return [0, 0];
36495             break;
36496             case "east":
36497                 return [0, 0];
36498             break;
36499             case "north":
36500                 return [0, 0];
36501             break;
36502             case "south":
36503                 return [0, 0];
36504             break;
36505         }
36506     },
36507
36508     getExpandAdj : function(){
36509         var c = this.collapsedEl, cm = this.cmargins;
36510         switch(this.position){
36511             case "west":
36512                 return [-(cm.right+c.getWidth()+cm.left), 0];
36513             break;
36514             case "east":
36515                 return [cm.right+c.getWidth()+cm.left, 0];
36516             break;
36517             case "north":
36518                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36519             break;
36520             case "south":
36521                 return [0, cm.top+cm.bottom+c.getHeight()];
36522             break;
36523         }
36524     }
36525 });/*
36526  * Based on:
36527  * Ext JS Library 1.1.1
36528  * Copyright(c) 2006-2007, Ext JS, LLC.
36529  *
36530  * Originally Released Under LGPL - original licence link has changed is not relivant.
36531  *
36532  * Fork - LGPL
36533  * <script type="text/javascript">
36534  */
36535 /*
36536  * These classes are private internal classes
36537  */
36538 Roo.bootstrap.layout.Center = function(config){
36539     config.region = "center";
36540     Roo.bootstrap.layout.Region.call(this, config);
36541     this.visible = true;
36542     this.minWidth = config.minWidth || 20;
36543     this.minHeight = config.minHeight || 20;
36544 };
36545
36546 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36547     hide : function(){
36548         // center panel can't be hidden
36549     },
36550     
36551     show : function(){
36552         // center panel can't be hidden
36553     },
36554     
36555     getMinWidth: function(){
36556         return this.minWidth;
36557     },
36558     
36559     getMinHeight: function(){
36560         return this.minHeight;
36561     }
36562 });
36563
36564
36565
36566
36567  
36568
36569
36570
36571
36572
36573 Roo.bootstrap.layout.North = function(config)
36574 {
36575     config.region = 'north';
36576     config.cursor = 'n-resize';
36577     
36578     Roo.bootstrap.layout.Split.call(this, config);
36579     
36580     
36581     if(this.split){
36582         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36583         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36584         this.split.el.addClass("roo-layout-split-v");
36585     }
36586     var size = config.initialSize || config.height;
36587     if(typeof size != "undefined"){
36588         this.el.setHeight(size);
36589     }
36590 };
36591 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36592 {
36593     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36594     
36595     
36596     
36597     getBox : function(){
36598         if(this.collapsed){
36599             return this.collapsedEl.getBox();
36600         }
36601         var box = this.el.getBox();
36602         if(this.split){
36603             box.height += this.split.el.getHeight();
36604         }
36605         return box;
36606     },
36607     
36608     updateBox : function(box){
36609         if(this.split && !this.collapsed){
36610             box.height -= this.split.el.getHeight();
36611             this.split.el.setLeft(box.x);
36612             this.split.el.setTop(box.y+box.height);
36613             this.split.el.setWidth(box.width);
36614         }
36615         if(this.collapsed){
36616             this.updateBody(box.width, null);
36617         }
36618         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36619     }
36620 });
36621
36622
36623
36624
36625
36626 Roo.bootstrap.layout.South = function(config){
36627     config.region = 'south';
36628     config.cursor = 's-resize';
36629     Roo.bootstrap.layout.Split.call(this, config);
36630     if(this.split){
36631         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36632         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36633         this.split.el.addClass("roo-layout-split-v");
36634     }
36635     var size = config.initialSize || config.height;
36636     if(typeof size != "undefined"){
36637         this.el.setHeight(size);
36638     }
36639 };
36640
36641 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36642     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36643     getBox : function(){
36644         if(this.collapsed){
36645             return this.collapsedEl.getBox();
36646         }
36647         var box = this.el.getBox();
36648         if(this.split){
36649             var sh = this.split.el.getHeight();
36650             box.height += sh;
36651             box.y -= sh;
36652         }
36653         return box;
36654     },
36655     
36656     updateBox : function(box){
36657         if(this.split && !this.collapsed){
36658             var sh = this.split.el.getHeight();
36659             box.height -= sh;
36660             box.y += sh;
36661             this.split.el.setLeft(box.x);
36662             this.split.el.setTop(box.y-sh);
36663             this.split.el.setWidth(box.width);
36664         }
36665         if(this.collapsed){
36666             this.updateBody(box.width, null);
36667         }
36668         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36669     }
36670 });
36671
36672 Roo.bootstrap.layout.East = function(config){
36673     config.region = "east";
36674     config.cursor = "e-resize";
36675     Roo.bootstrap.layout.Split.call(this, config);
36676     if(this.split){
36677         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36678         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36679         this.split.el.addClass("roo-layout-split-h");
36680     }
36681     var size = config.initialSize || config.width;
36682     if(typeof size != "undefined"){
36683         this.el.setWidth(size);
36684     }
36685 };
36686 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36687     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36688     getBox : function(){
36689         if(this.collapsed){
36690             return this.collapsedEl.getBox();
36691         }
36692         var box = this.el.getBox();
36693         if(this.split){
36694             var sw = this.split.el.getWidth();
36695             box.width += sw;
36696             box.x -= sw;
36697         }
36698         return box;
36699     },
36700
36701     updateBox : function(box){
36702         if(this.split && !this.collapsed){
36703             var sw = this.split.el.getWidth();
36704             box.width -= sw;
36705             this.split.el.setLeft(box.x);
36706             this.split.el.setTop(box.y);
36707             this.split.el.setHeight(box.height);
36708             box.x += sw;
36709         }
36710         if(this.collapsed){
36711             this.updateBody(null, box.height);
36712         }
36713         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36714     }
36715 });
36716
36717 Roo.bootstrap.layout.West = function(config){
36718     config.region = "west";
36719     config.cursor = "w-resize";
36720     
36721     Roo.bootstrap.layout.Split.call(this, config);
36722     if(this.split){
36723         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36724         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36725         this.split.el.addClass("roo-layout-split-h");
36726     }
36727     
36728 };
36729 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36730     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36731     
36732     onRender: function(ctr, pos)
36733     {
36734         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36735         var size = this.config.initialSize || this.config.width;
36736         if(typeof size != "undefined"){
36737             this.el.setWidth(size);
36738         }
36739     },
36740     
36741     getBox : function(){
36742         if(this.collapsed){
36743             return this.collapsedEl.getBox();
36744         }
36745         var box = this.el.getBox();
36746         if(this.split){
36747             box.width += this.split.el.getWidth();
36748         }
36749         return box;
36750     },
36751     
36752     updateBox : function(box){
36753         if(this.split && !this.collapsed){
36754             var sw = this.split.el.getWidth();
36755             box.width -= sw;
36756             this.split.el.setLeft(box.x+box.width);
36757             this.split.el.setTop(box.y);
36758             this.split.el.setHeight(box.height);
36759         }
36760         if(this.collapsed){
36761             this.updateBody(null, box.height);
36762         }
36763         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36764     }
36765 });
36766 Roo.namespace("Roo.bootstrap.panel");/*
36767  * Based on:
36768  * Ext JS Library 1.1.1
36769  * Copyright(c) 2006-2007, Ext JS, LLC.
36770  *
36771  * Originally Released Under LGPL - original licence link has changed is not relivant.
36772  *
36773  * Fork - LGPL
36774  * <script type="text/javascript">
36775  */
36776 /**
36777  * @class Roo.ContentPanel
36778  * @extends Roo.util.Observable
36779  * A basic ContentPanel element.
36780  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36781  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36782  * @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
36783  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36784  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36785  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36786  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36787  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36788  * @cfg {String} title          The title for this panel
36789  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36790  * @cfg {String} url            Calls {@link #setUrl} with this value
36791  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36792  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36793  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36794  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36795  * @cfg {Boolean} badges render the badges
36796
36797  * @constructor
36798  * Create a new ContentPanel.
36799  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36800  * @param {String/Object} config A string to set only the title or a config object
36801  * @param {String} content (optional) Set the HTML content for this panel
36802  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36803  */
36804 Roo.bootstrap.panel.Content = function( config){
36805     
36806     this.tpl = config.tpl || false;
36807     
36808     var el = config.el;
36809     var content = config.content;
36810
36811     if(config.autoCreate){ // xtype is available if this is called from factory
36812         el = Roo.id();
36813     }
36814     this.el = Roo.get(el);
36815     if(!this.el && config && config.autoCreate){
36816         if(typeof config.autoCreate == "object"){
36817             if(!config.autoCreate.id){
36818                 config.autoCreate.id = config.id||el;
36819             }
36820             this.el = Roo.DomHelper.append(document.body,
36821                         config.autoCreate, true);
36822         }else{
36823             var elcfg =  {   tag: "div",
36824                             cls: "roo-layout-inactive-content",
36825                             id: config.id||el
36826                             };
36827             if (config.html) {
36828                 elcfg.html = config.html;
36829                 
36830             }
36831                         
36832             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36833         }
36834     } 
36835     this.closable = false;
36836     this.loaded = false;
36837     this.active = false;
36838    
36839       
36840     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36841         
36842         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36843         
36844         this.wrapEl = this.el; //this.el.wrap();
36845         var ti = [];
36846         if (config.toolbar.items) {
36847             ti = config.toolbar.items ;
36848             delete config.toolbar.items ;
36849         }
36850         
36851         var nitems = [];
36852         this.toolbar.render(this.wrapEl, 'before');
36853         for(var i =0;i < ti.length;i++) {
36854           //  Roo.log(['add child', items[i]]);
36855             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36856         }
36857         this.toolbar.items = nitems;
36858         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36859         delete config.toolbar;
36860         
36861     }
36862     /*
36863     // xtype created footer. - not sure if will work as we normally have to render first..
36864     if (this.footer && !this.footer.el && this.footer.xtype) {
36865         if (!this.wrapEl) {
36866             this.wrapEl = this.el.wrap();
36867         }
36868     
36869         this.footer.container = this.wrapEl.createChild();
36870          
36871         this.footer = Roo.factory(this.footer, Roo);
36872         
36873     }
36874     */
36875     
36876      if(typeof config == "string"){
36877         this.title = config;
36878     }else{
36879         Roo.apply(this, config);
36880     }
36881     
36882     if(this.resizeEl){
36883         this.resizeEl = Roo.get(this.resizeEl, true);
36884     }else{
36885         this.resizeEl = this.el;
36886     }
36887     // handle view.xtype
36888     
36889  
36890     
36891     
36892     this.addEvents({
36893         /**
36894          * @event activate
36895          * Fires when this panel is activated. 
36896          * @param {Roo.ContentPanel} this
36897          */
36898         "activate" : true,
36899         /**
36900          * @event deactivate
36901          * Fires when this panel is activated. 
36902          * @param {Roo.ContentPanel} this
36903          */
36904         "deactivate" : true,
36905
36906         /**
36907          * @event resize
36908          * Fires when this panel is resized if fitToFrame is true.
36909          * @param {Roo.ContentPanel} this
36910          * @param {Number} width The width after any component adjustments
36911          * @param {Number} height The height after any component adjustments
36912          */
36913         "resize" : true,
36914         
36915          /**
36916          * @event render
36917          * Fires when this tab is created
36918          * @param {Roo.ContentPanel} this
36919          */
36920         "render" : true
36921         
36922         
36923         
36924     });
36925     
36926
36927     
36928     
36929     if(this.autoScroll){
36930         this.resizeEl.setStyle("overflow", "auto");
36931     } else {
36932         // fix randome scrolling
36933         //this.el.on('scroll', function() {
36934         //    Roo.log('fix random scolling');
36935         //    this.scrollTo('top',0); 
36936         //});
36937     }
36938     content = content || this.content;
36939     if(content){
36940         this.setContent(content);
36941     }
36942     if(config && config.url){
36943         this.setUrl(this.url, this.params, this.loadOnce);
36944     }
36945     
36946     
36947     
36948     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36949     
36950     if (this.view && typeof(this.view.xtype) != 'undefined') {
36951         this.view.el = this.el.appendChild(document.createElement("div"));
36952         this.view = Roo.factory(this.view); 
36953         this.view.render  &&  this.view.render(false, '');  
36954     }
36955     
36956     
36957     this.fireEvent('render', this);
36958 };
36959
36960 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36961     
36962     tabTip : '',
36963     
36964     setRegion : function(region){
36965         this.region = region;
36966         this.setActiveClass(region && !this.background);
36967     },
36968     
36969     
36970     setActiveClass: function(state)
36971     {
36972         if(state){
36973            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36974            this.el.setStyle('position','relative');
36975         }else{
36976            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36977            this.el.setStyle('position', 'absolute');
36978         } 
36979     },
36980     
36981     /**
36982      * Returns the toolbar for this Panel if one was configured. 
36983      * @return {Roo.Toolbar} 
36984      */
36985     getToolbar : function(){
36986         return this.toolbar;
36987     },
36988     
36989     setActiveState : function(active)
36990     {
36991         this.active = active;
36992         this.setActiveClass(active);
36993         if(!active){
36994             if(this.fireEvent("deactivate", this) === false){
36995                 return false;
36996             }
36997             return true;
36998         }
36999         this.fireEvent("activate", this);
37000         return true;
37001     },
37002     /**
37003      * Updates this panel's element
37004      * @param {String} content The new content
37005      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37006     */
37007     setContent : function(content, loadScripts){
37008         this.el.update(content, loadScripts);
37009     },
37010
37011     ignoreResize : function(w, h){
37012         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37013             return true;
37014         }else{
37015             this.lastSize = {width: w, height: h};
37016             return false;
37017         }
37018     },
37019     /**
37020      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37021      * @return {Roo.UpdateManager} The UpdateManager
37022      */
37023     getUpdateManager : function(){
37024         return this.el.getUpdateManager();
37025     },
37026      /**
37027      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37028      * @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:
37029 <pre><code>
37030 panel.load({
37031     url: "your-url.php",
37032     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37033     callback: yourFunction,
37034     scope: yourObject, //(optional scope)
37035     discardUrl: false,
37036     nocache: false,
37037     text: "Loading...",
37038     timeout: 30,
37039     scripts: false
37040 });
37041 </code></pre>
37042      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37043      * 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.
37044      * @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}
37045      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37046      * @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.
37047      * @return {Roo.ContentPanel} this
37048      */
37049     load : function(){
37050         var um = this.el.getUpdateManager();
37051         um.update.apply(um, arguments);
37052         return this;
37053     },
37054
37055
37056     /**
37057      * 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.
37058      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37059      * @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)
37060      * @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)
37061      * @return {Roo.UpdateManager} The UpdateManager
37062      */
37063     setUrl : function(url, params, loadOnce){
37064         if(this.refreshDelegate){
37065             this.removeListener("activate", this.refreshDelegate);
37066         }
37067         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37068         this.on("activate", this.refreshDelegate);
37069         return this.el.getUpdateManager();
37070     },
37071     
37072     _handleRefresh : function(url, params, loadOnce){
37073         if(!loadOnce || !this.loaded){
37074             var updater = this.el.getUpdateManager();
37075             updater.update(url, params, this._setLoaded.createDelegate(this));
37076         }
37077     },
37078     
37079     _setLoaded : function(){
37080         this.loaded = true;
37081     }, 
37082     
37083     /**
37084      * Returns this panel's id
37085      * @return {String} 
37086      */
37087     getId : function(){
37088         return this.el.id;
37089     },
37090     
37091     /** 
37092      * Returns this panel's element - used by regiosn to add.
37093      * @return {Roo.Element} 
37094      */
37095     getEl : function(){
37096         return this.wrapEl || this.el;
37097     },
37098     
37099    
37100     
37101     adjustForComponents : function(width, height)
37102     {
37103         //Roo.log('adjustForComponents ');
37104         if(this.resizeEl != this.el){
37105             width -= this.el.getFrameWidth('lr');
37106             height -= this.el.getFrameWidth('tb');
37107         }
37108         if(this.toolbar){
37109             var te = this.toolbar.getEl();
37110             te.setWidth(width);
37111             height -= te.getHeight();
37112         }
37113         if(this.footer){
37114             var te = this.footer.getEl();
37115             te.setWidth(width);
37116             height -= te.getHeight();
37117         }
37118         
37119         
37120         if(this.adjustments){
37121             width += this.adjustments[0];
37122             height += this.adjustments[1];
37123         }
37124         return {"width": width, "height": height};
37125     },
37126     
37127     setSize : function(width, height){
37128         if(this.fitToFrame && !this.ignoreResize(width, height)){
37129             if(this.fitContainer && this.resizeEl != this.el){
37130                 this.el.setSize(width, height);
37131             }
37132             var size = this.adjustForComponents(width, height);
37133             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37134             this.fireEvent('resize', this, size.width, size.height);
37135         }
37136     },
37137     
37138     /**
37139      * Returns this panel's title
37140      * @return {String} 
37141      */
37142     getTitle : function(){
37143         
37144         if (typeof(this.title) != 'object') {
37145             return this.title;
37146         }
37147         
37148         var t = '';
37149         for (var k in this.title) {
37150             if (!this.title.hasOwnProperty(k)) {
37151                 continue;
37152             }
37153             
37154             if (k.indexOf('-') >= 0) {
37155                 var s = k.split('-');
37156                 for (var i = 0; i<s.length; i++) {
37157                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37158                 }
37159             } else {
37160                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37161             }
37162         }
37163         return t;
37164     },
37165     
37166     /**
37167      * Set this panel's title
37168      * @param {String} title
37169      */
37170     setTitle : function(title){
37171         this.title = title;
37172         if(this.region){
37173             this.region.updatePanelTitle(this, title);
37174         }
37175     },
37176     
37177     /**
37178      * Returns true is this panel was configured to be closable
37179      * @return {Boolean} 
37180      */
37181     isClosable : function(){
37182         return this.closable;
37183     },
37184     
37185     beforeSlide : function(){
37186         this.el.clip();
37187         this.resizeEl.clip();
37188     },
37189     
37190     afterSlide : function(){
37191         this.el.unclip();
37192         this.resizeEl.unclip();
37193     },
37194     
37195     /**
37196      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37197      *   Will fail silently if the {@link #setUrl} method has not been called.
37198      *   This does not activate the panel, just updates its content.
37199      */
37200     refresh : function(){
37201         if(this.refreshDelegate){
37202            this.loaded = false;
37203            this.refreshDelegate();
37204         }
37205     },
37206     
37207     /**
37208      * Destroys this panel
37209      */
37210     destroy : function(){
37211         this.el.removeAllListeners();
37212         var tempEl = document.createElement("span");
37213         tempEl.appendChild(this.el.dom);
37214         tempEl.innerHTML = "";
37215         this.el.remove();
37216         this.el = null;
37217     },
37218     
37219     /**
37220      * form - if the content panel contains a form - this is a reference to it.
37221      * @type {Roo.form.Form}
37222      */
37223     form : false,
37224     /**
37225      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37226      *    This contains a reference to it.
37227      * @type {Roo.View}
37228      */
37229     view : false,
37230     
37231       /**
37232      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37233      * <pre><code>
37234
37235 layout.addxtype({
37236        xtype : 'Form',
37237        items: [ .... ]
37238    }
37239 );
37240
37241 </code></pre>
37242      * @param {Object} cfg Xtype definition of item to add.
37243      */
37244     
37245     
37246     getChildContainer: function () {
37247         return this.getEl();
37248     }
37249     
37250     
37251     /*
37252         var  ret = new Roo.factory(cfg);
37253         return ret;
37254         
37255         
37256         // add form..
37257         if (cfg.xtype.match(/^Form$/)) {
37258             
37259             var el;
37260             //if (this.footer) {
37261             //    el = this.footer.container.insertSibling(false, 'before');
37262             //} else {
37263                 el = this.el.createChild();
37264             //}
37265
37266             this.form = new  Roo.form.Form(cfg);
37267             
37268             
37269             if ( this.form.allItems.length) {
37270                 this.form.render(el.dom);
37271             }
37272             return this.form;
37273         }
37274         // should only have one of theses..
37275         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37276             // views.. should not be just added - used named prop 'view''
37277             
37278             cfg.el = this.el.appendChild(document.createElement("div"));
37279             // factory?
37280             
37281             var ret = new Roo.factory(cfg);
37282              
37283              ret.render && ret.render(false, ''); // render blank..
37284             this.view = ret;
37285             return ret;
37286         }
37287         return false;
37288     }
37289     \*/
37290 });
37291  
37292 /**
37293  * @class Roo.bootstrap.panel.Grid
37294  * @extends Roo.bootstrap.panel.Content
37295  * @constructor
37296  * Create a new GridPanel.
37297  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37298  * @param {Object} config A the config object
37299   
37300  */
37301
37302
37303
37304 Roo.bootstrap.panel.Grid = function(config)
37305 {
37306     
37307       
37308     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37309         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37310
37311     config.el = this.wrapper;
37312     //this.el = this.wrapper;
37313     
37314       if (config.container) {
37315         // ctor'ed from a Border/panel.grid
37316         
37317         
37318         this.wrapper.setStyle("overflow", "hidden");
37319         this.wrapper.addClass('roo-grid-container');
37320
37321     }
37322     
37323     
37324     if(config.toolbar){
37325         var tool_el = this.wrapper.createChild();    
37326         this.toolbar = Roo.factory(config.toolbar);
37327         var ti = [];
37328         if (config.toolbar.items) {
37329             ti = config.toolbar.items ;
37330             delete config.toolbar.items ;
37331         }
37332         
37333         var nitems = [];
37334         this.toolbar.render(tool_el);
37335         for(var i =0;i < ti.length;i++) {
37336           //  Roo.log(['add child', items[i]]);
37337             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37338         }
37339         this.toolbar.items = nitems;
37340         
37341         delete config.toolbar;
37342     }
37343     
37344     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37345     config.grid.scrollBody = true;;
37346     config.grid.monitorWindowResize = false; // turn off autosizing
37347     config.grid.autoHeight = false;
37348     config.grid.autoWidth = false;
37349     
37350     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37351     
37352     if (config.background) {
37353         // render grid on panel activation (if panel background)
37354         this.on('activate', function(gp) {
37355             if (!gp.grid.rendered) {
37356                 gp.grid.render(this.wrapper);
37357                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37358             }
37359         });
37360             
37361     } else {
37362         this.grid.render(this.wrapper);
37363         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37364
37365     }
37366     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37367     // ??? needed ??? config.el = this.wrapper;
37368     
37369     
37370     
37371   
37372     // xtype created footer. - not sure if will work as we normally have to render first..
37373     if (this.footer && !this.footer.el && this.footer.xtype) {
37374         
37375         var ctr = this.grid.getView().getFooterPanel(true);
37376         this.footer.dataSource = this.grid.dataSource;
37377         this.footer = Roo.factory(this.footer, Roo);
37378         this.footer.render(ctr);
37379         
37380     }
37381     
37382     
37383     
37384     
37385      
37386 };
37387
37388 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37389     getId : function(){
37390         return this.grid.id;
37391     },
37392     
37393     /**
37394      * Returns the grid for this panel
37395      * @return {Roo.bootstrap.Table} 
37396      */
37397     getGrid : function(){
37398         return this.grid;    
37399     },
37400     
37401     setSize : function(width, height){
37402         if(!this.ignoreResize(width, height)){
37403             var grid = this.grid;
37404             var size = this.adjustForComponents(width, height);
37405             var gridel = grid.getGridEl();
37406             gridel.setSize(size.width, size.height);
37407             /*
37408             var thd = grid.getGridEl().select('thead',true).first();
37409             var tbd = grid.getGridEl().select('tbody', true).first();
37410             if (tbd) {
37411                 tbd.setSize(width, height - thd.getHeight());
37412             }
37413             */
37414             grid.autoSize();
37415         }
37416     },
37417      
37418     
37419     
37420     beforeSlide : function(){
37421         this.grid.getView().scroller.clip();
37422     },
37423     
37424     afterSlide : function(){
37425         this.grid.getView().scroller.unclip();
37426     },
37427     
37428     destroy : function(){
37429         this.grid.destroy();
37430         delete this.grid;
37431         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37432     }
37433 });
37434
37435 /**
37436  * @class Roo.bootstrap.panel.Nest
37437  * @extends Roo.bootstrap.panel.Content
37438  * @constructor
37439  * Create a new Panel, that can contain a layout.Border.
37440  * 
37441  * 
37442  * @param {Roo.BorderLayout} layout The layout for this panel
37443  * @param {String/Object} config A string to set only the title or a config object
37444  */
37445 Roo.bootstrap.panel.Nest = function(config)
37446 {
37447     // construct with only one argument..
37448     /* FIXME - implement nicer consturctors
37449     if (layout.layout) {
37450         config = layout;
37451         layout = config.layout;
37452         delete config.layout;
37453     }
37454     if (layout.xtype && !layout.getEl) {
37455         // then layout needs constructing..
37456         layout = Roo.factory(layout, Roo);
37457     }
37458     */
37459     
37460     config.el =  config.layout.getEl();
37461     
37462     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37463     
37464     config.layout.monitorWindowResize = false; // turn off autosizing
37465     this.layout = config.layout;
37466     this.layout.getEl().addClass("roo-layout-nested-layout");
37467     
37468     
37469     
37470     
37471 };
37472
37473 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37474
37475     setSize : function(width, height){
37476         if(!this.ignoreResize(width, height)){
37477             var size = this.adjustForComponents(width, height);
37478             var el = this.layout.getEl();
37479             if (size.height < 1) {
37480                 el.setWidth(size.width);   
37481             } else {
37482                 el.setSize(size.width, size.height);
37483             }
37484             var touch = el.dom.offsetWidth;
37485             this.layout.layout();
37486             // ie requires a double layout on the first pass
37487             if(Roo.isIE && !this.initialized){
37488                 this.initialized = true;
37489                 this.layout.layout();
37490             }
37491         }
37492     },
37493     
37494     // activate all subpanels if not currently active..
37495     
37496     setActiveState : function(active){
37497         this.active = active;
37498         this.setActiveClass(active);
37499         
37500         if(!active){
37501             this.fireEvent("deactivate", this);
37502             return;
37503         }
37504         
37505         this.fireEvent("activate", this);
37506         // not sure if this should happen before or after..
37507         if (!this.layout) {
37508             return; // should not happen..
37509         }
37510         var reg = false;
37511         for (var r in this.layout.regions) {
37512             reg = this.layout.getRegion(r);
37513             if (reg.getActivePanel()) {
37514                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37515                 reg.setActivePanel(reg.getActivePanel());
37516                 continue;
37517             }
37518             if (!reg.panels.length) {
37519                 continue;
37520             }
37521             reg.showPanel(reg.getPanel(0));
37522         }
37523         
37524         
37525         
37526         
37527     },
37528     
37529     /**
37530      * Returns the nested BorderLayout for this panel
37531      * @return {Roo.BorderLayout} 
37532      */
37533     getLayout : function(){
37534         return this.layout;
37535     },
37536     
37537      /**
37538      * Adds a xtype elements to the layout of the nested panel
37539      * <pre><code>
37540
37541 panel.addxtype({
37542        xtype : 'ContentPanel',
37543        region: 'west',
37544        items: [ .... ]
37545    }
37546 );
37547
37548 panel.addxtype({
37549         xtype : 'NestedLayoutPanel',
37550         region: 'west',
37551         layout: {
37552            center: { },
37553            west: { }   
37554         },
37555         items : [ ... list of content panels or nested layout panels.. ]
37556    }
37557 );
37558 </code></pre>
37559      * @param {Object} cfg Xtype definition of item to add.
37560      */
37561     addxtype : function(cfg) {
37562         return this.layout.addxtype(cfg);
37563     
37564     }
37565 });        /*
37566  * Based on:
37567  * Ext JS Library 1.1.1
37568  * Copyright(c) 2006-2007, Ext JS, LLC.
37569  *
37570  * Originally Released Under LGPL - original licence link has changed is not relivant.
37571  *
37572  * Fork - LGPL
37573  * <script type="text/javascript">
37574  */
37575 /**
37576  * @class Roo.TabPanel
37577  * @extends Roo.util.Observable
37578  * A lightweight tab container.
37579  * <br><br>
37580  * Usage:
37581  * <pre><code>
37582 // basic tabs 1, built from existing content
37583 var tabs = new Roo.TabPanel("tabs1");
37584 tabs.addTab("script", "View Script");
37585 tabs.addTab("markup", "View Markup");
37586 tabs.activate("script");
37587
37588 // more advanced tabs, built from javascript
37589 var jtabs = new Roo.TabPanel("jtabs");
37590 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37591
37592 // set up the UpdateManager
37593 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37594 var updater = tab2.getUpdateManager();
37595 updater.setDefaultUrl("ajax1.htm");
37596 tab2.on('activate', updater.refresh, updater, true);
37597
37598 // Use setUrl for Ajax loading
37599 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37600 tab3.setUrl("ajax2.htm", null, true);
37601
37602 // Disabled tab
37603 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37604 tab4.disable();
37605
37606 jtabs.activate("jtabs-1");
37607  * </code></pre>
37608  * @constructor
37609  * Create a new TabPanel.
37610  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37611  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37612  */
37613 Roo.bootstrap.panel.Tabs = function(config){
37614     /**
37615     * The container element for this TabPanel.
37616     * @type Roo.Element
37617     */
37618     this.el = Roo.get(config.el);
37619     delete config.el;
37620     if(config){
37621         if(typeof config == "boolean"){
37622             this.tabPosition = config ? "bottom" : "top";
37623         }else{
37624             Roo.apply(this, config);
37625         }
37626     }
37627     
37628     if(this.tabPosition == "bottom"){
37629         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37630         this.el.addClass("roo-tabs-bottom");
37631     }
37632     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37633     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37634     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37635     if(Roo.isIE){
37636         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37637     }
37638     if(this.tabPosition != "bottom"){
37639         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37640          * @type Roo.Element
37641          */
37642         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37643         this.el.addClass("roo-tabs-top");
37644     }
37645     this.items = [];
37646
37647     this.bodyEl.setStyle("position", "relative");
37648
37649     this.active = null;
37650     this.activateDelegate = this.activate.createDelegate(this);
37651
37652     this.addEvents({
37653         /**
37654          * @event tabchange
37655          * Fires when the active tab changes
37656          * @param {Roo.TabPanel} this
37657          * @param {Roo.TabPanelItem} activePanel The new active tab
37658          */
37659         "tabchange": true,
37660         /**
37661          * @event beforetabchange
37662          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37663          * @param {Roo.TabPanel} this
37664          * @param {Object} e Set cancel to true on this object to cancel the tab change
37665          * @param {Roo.TabPanelItem} tab The tab being changed to
37666          */
37667         "beforetabchange" : true
37668     });
37669
37670     Roo.EventManager.onWindowResize(this.onResize, this);
37671     this.cpad = this.el.getPadding("lr");
37672     this.hiddenCount = 0;
37673
37674
37675     // toolbar on the tabbar support...
37676     if (this.toolbar) {
37677         alert("no toolbar support yet");
37678         this.toolbar  = false;
37679         /*
37680         var tcfg = this.toolbar;
37681         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37682         this.toolbar = new Roo.Toolbar(tcfg);
37683         if (Roo.isSafari) {
37684             var tbl = tcfg.container.child('table', true);
37685             tbl.setAttribute('width', '100%');
37686         }
37687         */
37688         
37689     }
37690    
37691
37692
37693     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37694 };
37695
37696 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37697     /*
37698      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37699      */
37700     tabPosition : "top",
37701     /*
37702      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37703      */
37704     currentTabWidth : 0,
37705     /*
37706      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37707      */
37708     minTabWidth : 40,
37709     /*
37710      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37711      */
37712     maxTabWidth : 250,
37713     /*
37714      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37715      */
37716     preferredTabWidth : 175,
37717     /*
37718      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37719      */
37720     resizeTabs : false,
37721     /*
37722      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37723      */
37724     monitorResize : true,
37725     /*
37726      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37727      */
37728     toolbar : false,
37729
37730     /**
37731      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37732      * @param {String} id The id of the div to use <b>or create</b>
37733      * @param {String} text The text for the tab
37734      * @param {String} content (optional) Content to put in the TabPanelItem body
37735      * @param {Boolean} closable (optional) True to create a close icon on the tab
37736      * @return {Roo.TabPanelItem} The created TabPanelItem
37737      */
37738     addTab : function(id, text, content, closable, tpl)
37739     {
37740         var item = new Roo.bootstrap.panel.TabItem({
37741             panel: this,
37742             id : id,
37743             text : text,
37744             closable : closable,
37745             tpl : tpl
37746         });
37747         this.addTabItem(item);
37748         if(content){
37749             item.setContent(content);
37750         }
37751         return item;
37752     },
37753
37754     /**
37755      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37756      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37757      * @return {Roo.TabPanelItem}
37758      */
37759     getTab : function(id){
37760         return this.items[id];
37761     },
37762
37763     /**
37764      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37765      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37766      */
37767     hideTab : function(id){
37768         var t = this.items[id];
37769         if(!t.isHidden()){
37770            t.setHidden(true);
37771            this.hiddenCount++;
37772            this.autoSizeTabs();
37773         }
37774     },
37775
37776     /**
37777      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37778      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37779      */
37780     unhideTab : function(id){
37781         var t = this.items[id];
37782         if(t.isHidden()){
37783            t.setHidden(false);
37784            this.hiddenCount--;
37785            this.autoSizeTabs();
37786         }
37787     },
37788
37789     /**
37790      * Adds an existing {@link Roo.TabPanelItem}.
37791      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37792      */
37793     addTabItem : function(item){
37794         this.items[item.id] = item;
37795         this.items.push(item);
37796       //  if(this.resizeTabs){
37797     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37798   //         this.autoSizeTabs();
37799 //        }else{
37800 //            item.autoSize();
37801        // }
37802     },
37803
37804     /**
37805      * Removes a {@link Roo.TabPanelItem}.
37806      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37807      */
37808     removeTab : function(id){
37809         var items = this.items;
37810         var tab = items[id];
37811         if(!tab) { return; }
37812         var index = items.indexOf(tab);
37813         if(this.active == tab && items.length > 1){
37814             var newTab = this.getNextAvailable(index);
37815             if(newTab) {
37816                 newTab.activate();
37817             }
37818         }
37819         this.stripEl.dom.removeChild(tab.pnode.dom);
37820         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37821             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37822         }
37823         items.splice(index, 1);
37824         delete this.items[tab.id];
37825         tab.fireEvent("close", tab);
37826         tab.purgeListeners();
37827         this.autoSizeTabs();
37828     },
37829
37830     getNextAvailable : function(start){
37831         var items = this.items;
37832         var index = start;
37833         // look for a next tab that will slide over to
37834         // replace the one being removed
37835         while(index < items.length){
37836             var item = items[++index];
37837             if(item && !item.isHidden()){
37838                 return item;
37839             }
37840         }
37841         // if one isn't found select the previous tab (on the left)
37842         index = start;
37843         while(index >= 0){
37844             var item = items[--index];
37845             if(item && !item.isHidden()){
37846                 return item;
37847             }
37848         }
37849         return null;
37850     },
37851
37852     /**
37853      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37854      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37855      */
37856     disableTab : function(id){
37857         var tab = this.items[id];
37858         if(tab && this.active != tab){
37859             tab.disable();
37860         }
37861     },
37862
37863     /**
37864      * Enables a {@link Roo.TabPanelItem} that is disabled.
37865      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37866      */
37867     enableTab : function(id){
37868         var tab = this.items[id];
37869         tab.enable();
37870     },
37871
37872     /**
37873      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37874      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37875      * @return {Roo.TabPanelItem} The TabPanelItem.
37876      */
37877     activate : function(id){
37878         var tab = this.items[id];
37879         if(!tab){
37880             return null;
37881         }
37882         if(tab == this.active || tab.disabled){
37883             return tab;
37884         }
37885         var e = {};
37886         this.fireEvent("beforetabchange", this, e, tab);
37887         if(e.cancel !== true && !tab.disabled){
37888             if(this.active){
37889                 this.active.hide();
37890             }
37891             this.active = this.items[id];
37892             this.active.show();
37893             this.fireEvent("tabchange", this, this.active);
37894         }
37895         return tab;
37896     },
37897
37898     /**
37899      * Gets the active {@link Roo.TabPanelItem}.
37900      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37901      */
37902     getActiveTab : function(){
37903         return this.active;
37904     },
37905
37906     /**
37907      * Updates the tab body element to fit the height of the container element
37908      * for overflow scrolling
37909      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37910      */
37911     syncHeight : function(targetHeight){
37912         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37913         var bm = this.bodyEl.getMargins();
37914         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37915         this.bodyEl.setHeight(newHeight);
37916         return newHeight;
37917     },
37918
37919     onResize : function(){
37920         if(this.monitorResize){
37921             this.autoSizeTabs();
37922         }
37923     },
37924
37925     /**
37926      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37927      */
37928     beginUpdate : function(){
37929         this.updating = true;
37930     },
37931
37932     /**
37933      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37934      */
37935     endUpdate : function(){
37936         this.updating = false;
37937         this.autoSizeTabs();
37938     },
37939
37940     /**
37941      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37942      */
37943     autoSizeTabs : function(){
37944         var count = this.items.length;
37945         var vcount = count - this.hiddenCount;
37946         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37947             return;
37948         }
37949         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37950         var availWidth = Math.floor(w / vcount);
37951         var b = this.stripBody;
37952         if(b.getWidth() > w){
37953             var tabs = this.items;
37954             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37955             if(availWidth < this.minTabWidth){
37956                 /*if(!this.sleft){    // incomplete scrolling code
37957                     this.createScrollButtons();
37958                 }
37959                 this.showScroll();
37960                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37961             }
37962         }else{
37963             if(this.currentTabWidth < this.preferredTabWidth){
37964                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37965             }
37966         }
37967     },
37968
37969     /**
37970      * Returns the number of tabs in this TabPanel.
37971      * @return {Number}
37972      */
37973      getCount : function(){
37974          return this.items.length;
37975      },
37976
37977     /**
37978      * Resizes all the tabs to the passed width
37979      * @param {Number} The new width
37980      */
37981     setTabWidth : function(width){
37982         this.currentTabWidth = width;
37983         for(var i = 0, len = this.items.length; i < len; i++) {
37984                 if(!this.items[i].isHidden()) {
37985                 this.items[i].setWidth(width);
37986             }
37987         }
37988     },
37989
37990     /**
37991      * Destroys this TabPanel
37992      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37993      */
37994     destroy : function(removeEl){
37995         Roo.EventManager.removeResizeListener(this.onResize, this);
37996         for(var i = 0, len = this.items.length; i < len; i++){
37997             this.items[i].purgeListeners();
37998         }
37999         if(removeEl === true){
38000             this.el.update("");
38001             this.el.remove();
38002         }
38003     },
38004     
38005     createStrip : function(container)
38006     {
38007         var strip = document.createElement("nav");
38008         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38009         container.appendChild(strip);
38010         return strip;
38011     },
38012     
38013     createStripList : function(strip)
38014     {
38015         // div wrapper for retard IE
38016         // returns the "tr" element.
38017         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38018         //'<div class="x-tabs-strip-wrap">'+
38019           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38020           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38021         return strip.firstChild; //.firstChild.firstChild.firstChild;
38022     },
38023     createBody : function(container)
38024     {
38025         var body = document.createElement("div");
38026         Roo.id(body, "tab-body");
38027         //Roo.fly(body).addClass("x-tabs-body");
38028         Roo.fly(body).addClass("tab-content");
38029         container.appendChild(body);
38030         return body;
38031     },
38032     createItemBody :function(bodyEl, id){
38033         var body = Roo.getDom(id);
38034         if(!body){
38035             body = document.createElement("div");
38036             body.id = id;
38037         }
38038         //Roo.fly(body).addClass("x-tabs-item-body");
38039         Roo.fly(body).addClass("tab-pane");
38040          bodyEl.insertBefore(body, bodyEl.firstChild);
38041         return body;
38042     },
38043     /** @private */
38044     createStripElements :  function(stripEl, text, closable, tpl)
38045     {
38046         var td = document.createElement("li"); // was td..
38047         
38048         
38049         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38050         
38051         
38052         stripEl.appendChild(td);
38053         /*if(closable){
38054             td.className = "x-tabs-closable";
38055             if(!this.closeTpl){
38056                 this.closeTpl = new Roo.Template(
38057                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38058                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38059                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38060                 );
38061             }
38062             var el = this.closeTpl.overwrite(td, {"text": text});
38063             var close = el.getElementsByTagName("div")[0];
38064             var inner = el.getElementsByTagName("em")[0];
38065             return {"el": el, "close": close, "inner": inner};
38066         } else {
38067         */
38068         // not sure what this is..
38069 //            if(!this.tabTpl){
38070                 //this.tabTpl = new Roo.Template(
38071                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38072                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38073                 //);
38074 //                this.tabTpl = new Roo.Template(
38075 //                   '<a href="#">' +
38076 //                   '<span unselectable="on"' +
38077 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38078 //                            ' >{text}</span></a>'
38079 //                );
38080 //                
38081 //            }
38082
38083
38084             var template = tpl || this.tabTpl || false;
38085             
38086             if(!template){
38087                 
38088                 template = new Roo.Template(
38089                    '<a href="#">' +
38090                    '<span unselectable="on"' +
38091                             (this.disableTooltips ? '' : ' title="{text}"') +
38092                             ' >{text}</span></a>'
38093                 );
38094             }
38095             
38096             switch (typeof(template)) {
38097                 case 'object' :
38098                     break;
38099                 case 'string' :
38100                     template = new Roo.Template(template);
38101                     break;
38102                 default :
38103                     break;
38104             }
38105             
38106             var el = template.overwrite(td, {"text": text});
38107             
38108             var inner = el.getElementsByTagName("span")[0];
38109             
38110             return {"el": el, "inner": inner};
38111             
38112     }
38113         
38114     
38115 });
38116
38117 /**
38118  * @class Roo.TabPanelItem
38119  * @extends Roo.util.Observable
38120  * Represents an individual item (tab plus body) in a TabPanel.
38121  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38122  * @param {String} id The id of this TabPanelItem
38123  * @param {String} text The text for the tab of this TabPanelItem
38124  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38125  */
38126 Roo.bootstrap.panel.TabItem = function(config){
38127     /**
38128      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38129      * @type Roo.TabPanel
38130      */
38131     this.tabPanel = config.panel;
38132     /**
38133      * The id for this TabPanelItem
38134      * @type String
38135      */
38136     this.id = config.id;
38137     /** @private */
38138     this.disabled = false;
38139     /** @private */
38140     this.text = config.text;
38141     /** @private */
38142     this.loaded = false;
38143     this.closable = config.closable;
38144
38145     /**
38146      * The body element for this TabPanelItem.
38147      * @type Roo.Element
38148      */
38149     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38150     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38151     this.bodyEl.setStyle("display", "block");
38152     this.bodyEl.setStyle("zoom", "1");
38153     //this.hideAction();
38154
38155     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38156     /** @private */
38157     this.el = Roo.get(els.el);
38158     this.inner = Roo.get(els.inner, true);
38159     this.textEl = Roo.get(this.el.dom.firstChild, true);
38160     this.pnode = Roo.get(els.el.parentNode, true);
38161 //    this.el.on("mousedown", this.onTabMouseDown, this);
38162     this.el.on("click", this.onTabClick, this);
38163     /** @private */
38164     if(config.closable){
38165         var c = Roo.get(els.close, true);
38166         c.dom.title = this.closeText;
38167         c.addClassOnOver("close-over");
38168         c.on("click", this.closeClick, this);
38169      }
38170
38171     this.addEvents({
38172          /**
38173          * @event activate
38174          * Fires when this tab becomes the active tab.
38175          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38176          * @param {Roo.TabPanelItem} this
38177          */
38178         "activate": true,
38179         /**
38180          * @event beforeclose
38181          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38182          * @param {Roo.TabPanelItem} this
38183          * @param {Object} e Set cancel to true on this object to cancel the close.
38184          */
38185         "beforeclose": true,
38186         /**
38187          * @event close
38188          * Fires when this tab is closed.
38189          * @param {Roo.TabPanelItem} this
38190          */
38191          "close": true,
38192         /**
38193          * @event deactivate
38194          * Fires when this tab is no longer the active tab.
38195          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38196          * @param {Roo.TabPanelItem} this
38197          */
38198          "deactivate" : true
38199     });
38200     this.hidden = false;
38201
38202     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38203 };
38204
38205 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38206            {
38207     purgeListeners : function(){
38208        Roo.util.Observable.prototype.purgeListeners.call(this);
38209        this.el.removeAllListeners();
38210     },
38211     /**
38212      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38213      */
38214     show : function(){
38215         this.pnode.addClass("active");
38216         this.showAction();
38217         if(Roo.isOpera){
38218             this.tabPanel.stripWrap.repaint();
38219         }
38220         this.fireEvent("activate", this.tabPanel, this);
38221     },
38222
38223     /**
38224      * Returns true if this tab is the active tab.
38225      * @return {Boolean}
38226      */
38227     isActive : function(){
38228         return this.tabPanel.getActiveTab() == this;
38229     },
38230
38231     /**
38232      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38233      */
38234     hide : function(){
38235         this.pnode.removeClass("active");
38236         this.hideAction();
38237         this.fireEvent("deactivate", this.tabPanel, this);
38238     },
38239
38240     hideAction : function(){
38241         this.bodyEl.hide();
38242         this.bodyEl.setStyle("position", "absolute");
38243         this.bodyEl.setLeft("-20000px");
38244         this.bodyEl.setTop("-20000px");
38245     },
38246
38247     showAction : function(){
38248         this.bodyEl.setStyle("position", "relative");
38249         this.bodyEl.setTop("");
38250         this.bodyEl.setLeft("");
38251         this.bodyEl.show();
38252     },
38253
38254     /**
38255      * Set the tooltip for the tab.
38256      * @param {String} tooltip The tab's tooltip
38257      */
38258     setTooltip : function(text){
38259         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38260             this.textEl.dom.qtip = text;
38261             this.textEl.dom.removeAttribute('title');
38262         }else{
38263             this.textEl.dom.title = text;
38264         }
38265     },
38266
38267     onTabClick : function(e){
38268         e.preventDefault();
38269         this.tabPanel.activate(this.id);
38270     },
38271
38272     onTabMouseDown : function(e){
38273         e.preventDefault();
38274         this.tabPanel.activate(this.id);
38275     },
38276 /*
38277     getWidth : function(){
38278         return this.inner.getWidth();
38279     },
38280
38281     setWidth : function(width){
38282         var iwidth = width - this.pnode.getPadding("lr");
38283         this.inner.setWidth(iwidth);
38284         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38285         this.pnode.setWidth(width);
38286     },
38287 */
38288     /**
38289      * Show or hide the tab
38290      * @param {Boolean} hidden True to hide or false to show.
38291      */
38292     setHidden : function(hidden){
38293         this.hidden = hidden;
38294         this.pnode.setStyle("display", hidden ? "none" : "");
38295     },
38296
38297     /**
38298      * Returns true if this tab is "hidden"
38299      * @return {Boolean}
38300      */
38301     isHidden : function(){
38302         return this.hidden;
38303     },
38304
38305     /**
38306      * Returns the text for this tab
38307      * @return {String}
38308      */
38309     getText : function(){
38310         return this.text;
38311     },
38312     /*
38313     autoSize : function(){
38314         //this.el.beginMeasure();
38315         this.textEl.setWidth(1);
38316         /*
38317          *  #2804 [new] Tabs in Roojs
38318          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38319          */
38320         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38321         //this.el.endMeasure();
38322     //},
38323
38324     /**
38325      * Sets the text for the tab (Note: this also sets the tooltip text)
38326      * @param {String} text The tab's text and tooltip
38327      */
38328     setText : function(text){
38329         this.text = text;
38330         this.textEl.update(text);
38331         this.setTooltip(text);
38332         //if(!this.tabPanel.resizeTabs){
38333         //    this.autoSize();
38334         //}
38335     },
38336     /**
38337      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38338      */
38339     activate : function(){
38340         this.tabPanel.activate(this.id);
38341     },
38342
38343     /**
38344      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38345      */
38346     disable : function(){
38347         if(this.tabPanel.active != this){
38348             this.disabled = true;
38349             this.pnode.addClass("disabled");
38350         }
38351     },
38352
38353     /**
38354      * Enables this TabPanelItem if it was previously disabled.
38355      */
38356     enable : function(){
38357         this.disabled = false;
38358         this.pnode.removeClass("disabled");
38359     },
38360
38361     /**
38362      * Sets the content for this TabPanelItem.
38363      * @param {String} content The content
38364      * @param {Boolean} loadScripts true to look for and load scripts
38365      */
38366     setContent : function(content, loadScripts){
38367         this.bodyEl.update(content, loadScripts);
38368     },
38369
38370     /**
38371      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38372      * @return {Roo.UpdateManager} The UpdateManager
38373      */
38374     getUpdateManager : function(){
38375         return this.bodyEl.getUpdateManager();
38376     },
38377
38378     /**
38379      * Set a URL to be used to load the content for this TabPanelItem.
38380      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38381      * @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)
38382      * @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)
38383      * @return {Roo.UpdateManager} The UpdateManager
38384      */
38385     setUrl : function(url, params, loadOnce){
38386         if(this.refreshDelegate){
38387             this.un('activate', this.refreshDelegate);
38388         }
38389         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38390         this.on("activate", this.refreshDelegate);
38391         return this.bodyEl.getUpdateManager();
38392     },
38393
38394     /** @private */
38395     _handleRefresh : function(url, params, loadOnce){
38396         if(!loadOnce || !this.loaded){
38397             var updater = this.bodyEl.getUpdateManager();
38398             updater.update(url, params, this._setLoaded.createDelegate(this));
38399         }
38400     },
38401
38402     /**
38403      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38404      *   Will fail silently if the setUrl method has not been called.
38405      *   This does not activate the panel, just updates its content.
38406      */
38407     refresh : function(){
38408         if(this.refreshDelegate){
38409            this.loaded = false;
38410            this.refreshDelegate();
38411         }
38412     },
38413
38414     /** @private */
38415     _setLoaded : function(){
38416         this.loaded = true;
38417     },
38418
38419     /** @private */
38420     closeClick : function(e){
38421         var o = {};
38422         e.stopEvent();
38423         this.fireEvent("beforeclose", this, o);
38424         if(o.cancel !== true){
38425             this.tabPanel.removeTab(this.id);
38426         }
38427     },
38428     /**
38429      * The text displayed in the tooltip for the close icon.
38430      * @type String
38431      */
38432     closeText : "Close this tab"
38433 });
38434 /**
38435 *    This script refer to:
38436 *    Title: International Telephone Input
38437 *    Author: Jack O'Connor
38438 *    Code version:  v12.1.12
38439 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38440 **/
38441
38442 Roo.bootstrap.PhoneInputData = function() {
38443     var d = [
38444       [
38445         "Afghanistan (‫افغانستان‬‎)",
38446         "af",
38447         "93"
38448       ],
38449       [
38450         "Albania (Shqipëri)",
38451         "al",
38452         "355"
38453       ],
38454       [
38455         "Algeria (‫الجزائر‬‎)",
38456         "dz",
38457         "213"
38458       ],
38459       [
38460         "American Samoa",
38461         "as",
38462         "1684"
38463       ],
38464       [
38465         "Andorra",
38466         "ad",
38467         "376"
38468       ],
38469       [
38470         "Angola",
38471         "ao",
38472         "244"
38473       ],
38474       [
38475         "Anguilla",
38476         "ai",
38477         "1264"
38478       ],
38479       [
38480         "Antigua and Barbuda",
38481         "ag",
38482         "1268"
38483       ],
38484       [
38485         "Argentina",
38486         "ar",
38487         "54"
38488       ],
38489       [
38490         "Armenia (Հայաստան)",
38491         "am",
38492         "374"
38493       ],
38494       [
38495         "Aruba",
38496         "aw",
38497         "297"
38498       ],
38499       [
38500         "Australia",
38501         "au",
38502         "61",
38503         0
38504       ],
38505       [
38506         "Austria (Österreich)",
38507         "at",
38508         "43"
38509       ],
38510       [
38511         "Azerbaijan (Azərbaycan)",
38512         "az",
38513         "994"
38514       ],
38515       [
38516         "Bahamas",
38517         "bs",
38518         "1242"
38519       ],
38520       [
38521         "Bahrain (‫البحرين‬‎)",
38522         "bh",
38523         "973"
38524       ],
38525       [
38526         "Bangladesh (বাংলাদেশ)",
38527         "bd",
38528         "880"
38529       ],
38530       [
38531         "Barbados",
38532         "bb",
38533         "1246"
38534       ],
38535       [
38536         "Belarus (Беларусь)",
38537         "by",
38538         "375"
38539       ],
38540       [
38541         "Belgium (België)",
38542         "be",
38543         "32"
38544       ],
38545       [
38546         "Belize",
38547         "bz",
38548         "501"
38549       ],
38550       [
38551         "Benin (Bénin)",
38552         "bj",
38553         "229"
38554       ],
38555       [
38556         "Bermuda",
38557         "bm",
38558         "1441"
38559       ],
38560       [
38561         "Bhutan (འབྲུག)",
38562         "bt",
38563         "975"
38564       ],
38565       [
38566         "Bolivia",
38567         "bo",
38568         "591"
38569       ],
38570       [
38571         "Bosnia and Herzegovina (Босна и Херцеговина)",
38572         "ba",
38573         "387"
38574       ],
38575       [
38576         "Botswana",
38577         "bw",
38578         "267"
38579       ],
38580       [
38581         "Brazil (Brasil)",
38582         "br",
38583         "55"
38584       ],
38585       [
38586         "British Indian Ocean Territory",
38587         "io",
38588         "246"
38589       ],
38590       [
38591         "British Virgin Islands",
38592         "vg",
38593         "1284"
38594       ],
38595       [
38596         "Brunei",
38597         "bn",
38598         "673"
38599       ],
38600       [
38601         "Bulgaria (България)",
38602         "bg",
38603         "359"
38604       ],
38605       [
38606         "Burkina Faso",
38607         "bf",
38608         "226"
38609       ],
38610       [
38611         "Burundi (Uburundi)",
38612         "bi",
38613         "257"
38614       ],
38615       [
38616         "Cambodia (កម្ពុជា)",
38617         "kh",
38618         "855"
38619       ],
38620       [
38621         "Cameroon (Cameroun)",
38622         "cm",
38623         "237"
38624       ],
38625       [
38626         "Canada",
38627         "ca",
38628         "1",
38629         1,
38630         ["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"]
38631       ],
38632       [
38633         "Cape Verde (Kabu Verdi)",
38634         "cv",
38635         "238"
38636       ],
38637       [
38638         "Caribbean Netherlands",
38639         "bq",
38640         "599",
38641         1
38642       ],
38643       [
38644         "Cayman Islands",
38645         "ky",
38646         "1345"
38647       ],
38648       [
38649         "Central African Republic (République centrafricaine)",
38650         "cf",
38651         "236"
38652       ],
38653       [
38654         "Chad (Tchad)",
38655         "td",
38656         "235"
38657       ],
38658       [
38659         "Chile",
38660         "cl",
38661         "56"
38662       ],
38663       [
38664         "China (中国)",
38665         "cn",
38666         "86"
38667       ],
38668       [
38669         "Christmas Island",
38670         "cx",
38671         "61",
38672         2
38673       ],
38674       [
38675         "Cocos (Keeling) Islands",
38676         "cc",
38677         "61",
38678         1
38679       ],
38680       [
38681         "Colombia",
38682         "co",
38683         "57"
38684       ],
38685       [
38686         "Comoros (‫جزر القمر‬‎)",
38687         "km",
38688         "269"
38689       ],
38690       [
38691         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38692         "cd",
38693         "243"
38694       ],
38695       [
38696         "Congo (Republic) (Congo-Brazzaville)",
38697         "cg",
38698         "242"
38699       ],
38700       [
38701         "Cook Islands",
38702         "ck",
38703         "682"
38704       ],
38705       [
38706         "Costa Rica",
38707         "cr",
38708         "506"
38709       ],
38710       [
38711         "Côte d’Ivoire",
38712         "ci",
38713         "225"
38714       ],
38715       [
38716         "Croatia (Hrvatska)",
38717         "hr",
38718         "385"
38719       ],
38720       [
38721         "Cuba",
38722         "cu",
38723         "53"
38724       ],
38725       [
38726         "Curaçao",
38727         "cw",
38728         "599",
38729         0
38730       ],
38731       [
38732         "Cyprus (Κύπρος)",
38733         "cy",
38734         "357"
38735       ],
38736       [
38737         "Czech Republic (Česká republika)",
38738         "cz",
38739         "420"
38740       ],
38741       [
38742         "Denmark (Danmark)",
38743         "dk",
38744         "45"
38745       ],
38746       [
38747         "Djibouti",
38748         "dj",
38749         "253"
38750       ],
38751       [
38752         "Dominica",
38753         "dm",
38754         "1767"
38755       ],
38756       [
38757         "Dominican Republic (República Dominicana)",
38758         "do",
38759         "1",
38760         2,
38761         ["809", "829", "849"]
38762       ],
38763       [
38764         "Ecuador",
38765         "ec",
38766         "593"
38767       ],
38768       [
38769         "Egypt (‫مصر‬‎)",
38770         "eg",
38771         "20"
38772       ],
38773       [
38774         "El Salvador",
38775         "sv",
38776         "503"
38777       ],
38778       [
38779         "Equatorial Guinea (Guinea Ecuatorial)",
38780         "gq",
38781         "240"
38782       ],
38783       [
38784         "Eritrea",
38785         "er",
38786         "291"
38787       ],
38788       [
38789         "Estonia (Eesti)",
38790         "ee",
38791         "372"
38792       ],
38793       [
38794         "Ethiopia",
38795         "et",
38796         "251"
38797       ],
38798       [
38799         "Falkland Islands (Islas Malvinas)",
38800         "fk",
38801         "500"
38802       ],
38803       [
38804         "Faroe Islands (Føroyar)",
38805         "fo",
38806         "298"
38807       ],
38808       [
38809         "Fiji",
38810         "fj",
38811         "679"
38812       ],
38813       [
38814         "Finland (Suomi)",
38815         "fi",
38816         "358",
38817         0
38818       ],
38819       [
38820         "France",
38821         "fr",
38822         "33"
38823       ],
38824       [
38825         "French Guiana (Guyane française)",
38826         "gf",
38827         "594"
38828       ],
38829       [
38830         "French Polynesia (Polynésie française)",
38831         "pf",
38832         "689"
38833       ],
38834       [
38835         "Gabon",
38836         "ga",
38837         "241"
38838       ],
38839       [
38840         "Gambia",
38841         "gm",
38842         "220"
38843       ],
38844       [
38845         "Georgia (საქართველო)",
38846         "ge",
38847         "995"
38848       ],
38849       [
38850         "Germany (Deutschland)",
38851         "de",
38852         "49"
38853       ],
38854       [
38855         "Ghana (Gaana)",
38856         "gh",
38857         "233"
38858       ],
38859       [
38860         "Gibraltar",
38861         "gi",
38862         "350"
38863       ],
38864       [
38865         "Greece (Ελλάδα)",
38866         "gr",
38867         "30"
38868       ],
38869       [
38870         "Greenland (Kalaallit Nunaat)",
38871         "gl",
38872         "299"
38873       ],
38874       [
38875         "Grenada",
38876         "gd",
38877         "1473"
38878       ],
38879       [
38880         "Guadeloupe",
38881         "gp",
38882         "590",
38883         0
38884       ],
38885       [
38886         "Guam",
38887         "gu",
38888         "1671"
38889       ],
38890       [
38891         "Guatemala",
38892         "gt",
38893         "502"
38894       ],
38895       [
38896         "Guernsey",
38897         "gg",
38898         "44",
38899         1
38900       ],
38901       [
38902         "Guinea (Guinée)",
38903         "gn",
38904         "224"
38905       ],
38906       [
38907         "Guinea-Bissau (Guiné Bissau)",
38908         "gw",
38909         "245"
38910       ],
38911       [
38912         "Guyana",
38913         "gy",
38914         "592"
38915       ],
38916       [
38917         "Haiti",
38918         "ht",
38919         "509"
38920       ],
38921       [
38922         "Honduras",
38923         "hn",
38924         "504"
38925       ],
38926       [
38927         "Hong Kong (香港)",
38928         "hk",
38929         "852"
38930       ],
38931       [
38932         "Hungary (Magyarország)",
38933         "hu",
38934         "36"
38935       ],
38936       [
38937         "Iceland (Ísland)",
38938         "is",
38939         "354"
38940       ],
38941       [
38942         "India (भारत)",
38943         "in",
38944         "91"
38945       ],
38946       [
38947         "Indonesia",
38948         "id",
38949         "62"
38950       ],
38951       [
38952         "Iran (‫ایران‬‎)",
38953         "ir",
38954         "98"
38955       ],
38956       [
38957         "Iraq (‫العراق‬‎)",
38958         "iq",
38959         "964"
38960       ],
38961       [
38962         "Ireland",
38963         "ie",
38964         "353"
38965       ],
38966       [
38967         "Isle of Man",
38968         "im",
38969         "44",
38970         2
38971       ],
38972       [
38973         "Israel (‫ישראל‬‎)",
38974         "il",
38975         "972"
38976       ],
38977       [
38978         "Italy (Italia)",
38979         "it",
38980         "39",
38981         0
38982       ],
38983       [
38984         "Jamaica",
38985         "jm",
38986         "1876"
38987       ],
38988       [
38989         "Japan (日本)",
38990         "jp",
38991         "81"
38992       ],
38993       [
38994         "Jersey",
38995         "je",
38996         "44",
38997         3
38998       ],
38999       [
39000         "Jordan (‫الأردن‬‎)",
39001         "jo",
39002         "962"
39003       ],
39004       [
39005         "Kazakhstan (Казахстан)",
39006         "kz",
39007         "7",
39008         1
39009       ],
39010       [
39011         "Kenya",
39012         "ke",
39013         "254"
39014       ],
39015       [
39016         "Kiribati",
39017         "ki",
39018         "686"
39019       ],
39020       [
39021         "Kosovo",
39022         "xk",
39023         "383"
39024       ],
39025       [
39026         "Kuwait (‫الكويت‬‎)",
39027         "kw",
39028         "965"
39029       ],
39030       [
39031         "Kyrgyzstan (Кыргызстан)",
39032         "kg",
39033         "996"
39034       ],
39035       [
39036         "Laos (ລາວ)",
39037         "la",
39038         "856"
39039       ],
39040       [
39041         "Latvia (Latvija)",
39042         "lv",
39043         "371"
39044       ],
39045       [
39046         "Lebanon (‫لبنان‬‎)",
39047         "lb",
39048         "961"
39049       ],
39050       [
39051         "Lesotho",
39052         "ls",
39053         "266"
39054       ],
39055       [
39056         "Liberia",
39057         "lr",
39058         "231"
39059       ],
39060       [
39061         "Libya (‫ليبيا‬‎)",
39062         "ly",
39063         "218"
39064       ],
39065       [
39066         "Liechtenstein",
39067         "li",
39068         "423"
39069       ],
39070       [
39071         "Lithuania (Lietuva)",
39072         "lt",
39073         "370"
39074       ],
39075       [
39076         "Luxembourg",
39077         "lu",
39078         "352"
39079       ],
39080       [
39081         "Macau (澳門)",
39082         "mo",
39083         "853"
39084       ],
39085       [
39086         "Macedonia (FYROM) (Македонија)",
39087         "mk",
39088         "389"
39089       ],
39090       [
39091         "Madagascar (Madagasikara)",
39092         "mg",
39093         "261"
39094       ],
39095       [
39096         "Malawi",
39097         "mw",
39098         "265"
39099       ],
39100       [
39101         "Malaysia",
39102         "my",
39103         "60"
39104       ],
39105       [
39106         "Maldives",
39107         "mv",
39108         "960"
39109       ],
39110       [
39111         "Mali",
39112         "ml",
39113         "223"
39114       ],
39115       [
39116         "Malta",
39117         "mt",
39118         "356"
39119       ],
39120       [
39121         "Marshall Islands",
39122         "mh",
39123         "692"
39124       ],
39125       [
39126         "Martinique",
39127         "mq",
39128         "596"
39129       ],
39130       [
39131         "Mauritania (‫موريتانيا‬‎)",
39132         "mr",
39133         "222"
39134       ],
39135       [
39136         "Mauritius (Moris)",
39137         "mu",
39138         "230"
39139       ],
39140       [
39141         "Mayotte",
39142         "yt",
39143         "262",
39144         1
39145       ],
39146       [
39147         "Mexico (México)",
39148         "mx",
39149         "52"
39150       ],
39151       [
39152         "Micronesia",
39153         "fm",
39154         "691"
39155       ],
39156       [
39157         "Moldova (Republica Moldova)",
39158         "md",
39159         "373"
39160       ],
39161       [
39162         "Monaco",
39163         "mc",
39164         "377"
39165       ],
39166       [
39167         "Mongolia (Монгол)",
39168         "mn",
39169         "976"
39170       ],
39171       [
39172         "Montenegro (Crna Gora)",
39173         "me",
39174         "382"
39175       ],
39176       [
39177         "Montserrat",
39178         "ms",
39179         "1664"
39180       ],
39181       [
39182         "Morocco (‫المغرب‬‎)",
39183         "ma",
39184         "212",
39185         0
39186       ],
39187       [
39188         "Mozambique (Moçambique)",
39189         "mz",
39190         "258"
39191       ],
39192       [
39193         "Myanmar (Burma) (မြန်မာ)",
39194         "mm",
39195         "95"
39196       ],
39197       [
39198         "Namibia (Namibië)",
39199         "na",
39200         "264"
39201       ],
39202       [
39203         "Nauru",
39204         "nr",
39205         "674"
39206       ],
39207       [
39208         "Nepal (नेपाल)",
39209         "np",
39210         "977"
39211       ],
39212       [
39213         "Netherlands (Nederland)",
39214         "nl",
39215         "31"
39216       ],
39217       [
39218         "New Caledonia (Nouvelle-Calédonie)",
39219         "nc",
39220         "687"
39221       ],
39222       [
39223         "New Zealand",
39224         "nz",
39225         "64"
39226       ],
39227       [
39228         "Nicaragua",
39229         "ni",
39230         "505"
39231       ],
39232       [
39233         "Niger (Nijar)",
39234         "ne",
39235         "227"
39236       ],
39237       [
39238         "Nigeria",
39239         "ng",
39240         "234"
39241       ],
39242       [
39243         "Niue",
39244         "nu",
39245         "683"
39246       ],
39247       [
39248         "Norfolk Island",
39249         "nf",
39250         "672"
39251       ],
39252       [
39253         "North Korea (조선 민주주의 인민 공화국)",
39254         "kp",
39255         "850"
39256       ],
39257       [
39258         "Northern Mariana Islands",
39259         "mp",
39260         "1670"
39261       ],
39262       [
39263         "Norway (Norge)",
39264         "no",
39265         "47",
39266         0
39267       ],
39268       [
39269         "Oman (‫عُمان‬‎)",
39270         "om",
39271         "968"
39272       ],
39273       [
39274         "Pakistan (‫پاکستان‬‎)",
39275         "pk",
39276         "92"
39277       ],
39278       [
39279         "Palau",
39280         "pw",
39281         "680"
39282       ],
39283       [
39284         "Palestine (‫فلسطين‬‎)",
39285         "ps",
39286         "970"
39287       ],
39288       [
39289         "Panama (Panamá)",
39290         "pa",
39291         "507"
39292       ],
39293       [
39294         "Papua New Guinea",
39295         "pg",
39296         "675"
39297       ],
39298       [
39299         "Paraguay",
39300         "py",
39301         "595"
39302       ],
39303       [
39304         "Peru (Perú)",
39305         "pe",
39306         "51"
39307       ],
39308       [
39309         "Philippines",
39310         "ph",
39311         "63"
39312       ],
39313       [
39314         "Poland (Polska)",
39315         "pl",
39316         "48"
39317       ],
39318       [
39319         "Portugal",
39320         "pt",
39321         "351"
39322       ],
39323       [
39324         "Puerto Rico",
39325         "pr",
39326         "1",
39327         3,
39328         ["787", "939"]
39329       ],
39330       [
39331         "Qatar (‫قطر‬‎)",
39332         "qa",
39333         "974"
39334       ],
39335       [
39336         "Réunion (La Réunion)",
39337         "re",
39338         "262",
39339         0
39340       ],
39341       [
39342         "Romania (România)",
39343         "ro",
39344         "40"
39345       ],
39346       [
39347         "Russia (Россия)",
39348         "ru",
39349         "7",
39350         0
39351       ],
39352       [
39353         "Rwanda",
39354         "rw",
39355         "250"
39356       ],
39357       [
39358         "Saint Barthélemy",
39359         "bl",
39360         "590",
39361         1
39362       ],
39363       [
39364         "Saint Helena",
39365         "sh",
39366         "290"
39367       ],
39368       [
39369         "Saint Kitts and Nevis",
39370         "kn",
39371         "1869"
39372       ],
39373       [
39374         "Saint Lucia",
39375         "lc",
39376         "1758"
39377       ],
39378       [
39379         "Saint Martin (Saint-Martin (partie française))",
39380         "mf",
39381         "590",
39382         2
39383       ],
39384       [
39385         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39386         "pm",
39387         "508"
39388       ],
39389       [
39390         "Saint Vincent and the Grenadines",
39391         "vc",
39392         "1784"
39393       ],
39394       [
39395         "Samoa",
39396         "ws",
39397         "685"
39398       ],
39399       [
39400         "San Marino",
39401         "sm",
39402         "378"
39403       ],
39404       [
39405         "São Tomé and Príncipe (São Tomé e Príncipe)",
39406         "st",
39407         "239"
39408       ],
39409       [
39410         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39411         "sa",
39412         "966"
39413       ],
39414       [
39415         "Senegal (Sénégal)",
39416         "sn",
39417         "221"
39418       ],
39419       [
39420         "Serbia (Србија)",
39421         "rs",
39422         "381"
39423       ],
39424       [
39425         "Seychelles",
39426         "sc",
39427         "248"
39428       ],
39429       [
39430         "Sierra Leone",
39431         "sl",
39432         "232"
39433       ],
39434       [
39435         "Singapore",
39436         "sg",
39437         "65"
39438       ],
39439       [
39440         "Sint Maarten",
39441         "sx",
39442         "1721"
39443       ],
39444       [
39445         "Slovakia (Slovensko)",
39446         "sk",
39447         "421"
39448       ],
39449       [
39450         "Slovenia (Slovenija)",
39451         "si",
39452         "386"
39453       ],
39454       [
39455         "Solomon Islands",
39456         "sb",
39457         "677"
39458       ],
39459       [
39460         "Somalia (Soomaaliya)",
39461         "so",
39462         "252"
39463       ],
39464       [
39465         "South Africa",
39466         "za",
39467         "27"
39468       ],
39469       [
39470         "South Korea (대한민국)",
39471         "kr",
39472         "82"
39473       ],
39474       [
39475         "South Sudan (‫جنوب السودان‬‎)",
39476         "ss",
39477         "211"
39478       ],
39479       [
39480         "Spain (España)",
39481         "es",
39482         "34"
39483       ],
39484       [
39485         "Sri Lanka (ශ්‍රී ලංකාව)",
39486         "lk",
39487         "94"
39488       ],
39489       [
39490         "Sudan (‫السودان‬‎)",
39491         "sd",
39492         "249"
39493       ],
39494       [
39495         "Suriname",
39496         "sr",
39497         "597"
39498       ],
39499       [
39500         "Svalbard and Jan Mayen",
39501         "sj",
39502         "47",
39503         1
39504       ],
39505       [
39506         "Swaziland",
39507         "sz",
39508         "268"
39509       ],
39510       [
39511         "Sweden (Sverige)",
39512         "se",
39513         "46"
39514       ],
39515       [
39516         "Switzerland (Schweiz)",
39517         "ch",
39518         "41"
39519       ],
39520       [
39521         "Syria (‫سوريا‬‎)",
39522         "sy",
39523         "963"
39524       ],
39525       [
39526         "Taiwan (台灣)",
39527         "tw",
39528         "886"
39529       ],
39530       [
39531         "Tajikistan",
39532         "tj",
39533         "992"
39534       ],
39535       [
39536         "Tanzania",
39537         "tz",
39538         "255"
39539       ],
39540       [
39541         "Thailand (ไทย)",
39542         "th",
39543         "66"
39544       ],
39545       [
39546         "Timor-Leste",
39547         "tl",
39548         "670"
39549       ],
39550       [
39551         "Togo",
39552         "tg",
39553         "228"
39554       ],
39555       [
39556         "Tokelau",
39557         "tk",
39558         "690"
39559       ],
39560       [
39561         "Tonga",
39562         "to",
39563         "676"
39564       ],
39565       [
39566         "Trinidad and Tobago",
39567         "tt",
39568         "1868"
39569       ],
39570       [
39571         "Tunisia (‫تونس‬‎)",
39572         "tn",
39573         "216"
39574       ],
39575       [
39576         "Turkey (Türkiye)",
39577         "tr",
39578         "90"
39579       ],
39580       [
39581         "Turkmenistan",
39582         "tm",
39583         "993"
39584       ],
39585       [
39586         "Turks and Caicos Islands",
39587         "tc",
39588         "1649"
39589       ],
39590       [
39591         "Tuvalu",
39592         "tv",
39593         "688"
39594       ],
39595       [
39596         "U.S. Virgin Islands",
39597         "vi",
39598         "1340"
39599       ],
39600       [
39601         "Uganda",
39602         "ug",
39603         "256"
39604       ],
39605       [
39606         "Ukraine (Україна)",
39607         "ua",
39608         "380"
39609       ],
39610       [
39611         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39612         "ae",
39613         "971"
39614       ],
39615       [
39616         "United Kingdom",
39617         "gb",
39618         "44",
39619         0
39620       ],
39621       [
39622         "United States",
39623         "us",
39624         "1",
39625         0
39626       ],
39627       [
39628         "Uruguay",
39629         "uy",
39630         "598"
39631       ],
39632       [
39633         "Uzbekistan (Oʻzbekiston)",
39634         "uz",
39635         "998"
39636       ],
39637       [
39638         "Vanuatu",
39639         "vu",
39640         "678"
39641       ],
39642       [
39643         "Vatican City (Città del Vaticano)",
39644         "va",
39645         "39",
39646         1
39647       ],
39648       [
39649         "Venezuela",
39650         "ve",
39651         "58"
39652       ],
39653       [
39654         "Vietnam (Việt Nam)",
39655         "vn",
39656         "84"
39657       ],
39658       [
39659         "Wallis and Futuna (Wallis-et-Futuna)",
39660         "wf",
39661         "681"
39662       ],
39663       [
39664         "Western Sahara (‫الصحراء الغربية‬‎)",
39665         "eh",
39666         "212",
39667         1
39668       ],
39669       [
39670         "Yemen (‫اليمن‬‎)",
39671         "ye",
39672         "967"
39673       ],
39674       [
39675         "Zambia",
39676         "zm",
39677         "260"
39678       ],
39679       [
39680         "Zimbabwe",
39681         "zw",
39682         "263"
39683       ],
39684       [
39685         "Åland Islands",
39686         "ax",
39687         "358",
39688         1
39689       ]
39690   ];
39691   
39692   return d;
39693 }/**
39694 *    This script refer to:
39695 *    Title: International Telephone Input
39696 *    Author: Jack O'Connor
39697 *    Code version:  v12.1.12
39698 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39699 **/
39700
39701 /**
39702  * @class Roo.bootstrap.PhoneInput
39703  * @extends Roo.bootstrap.TriggerField
39704  * An input with International dial-code selection
39705  
39706  * @cfg {String} defaultDialCode default '+852'
39707  * @cfg {Array} preferedCountries default []
39708   
39709  * @constructor
39710  * Create a new PhoneInput.
39711  * @param {Object} config Configuration options
39712  */
39713
39714 Roo.bootstrap.PhoneInput = function(config) {
39715     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39716 };
39717
39718 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39719         
39720         listWidth: undefined,
39721         
39722         selectedClass: 'active',
39723         
39724         invalidClass : "has-warning",
39725         
39726         validClass: 'has-success',
39727         
39728         allowed: '0123456789',
39729         
39730         /**
39731          * @cfg {String} defaultDialCode The default dial code when initializing the input
39732          */
39733         defaultDialCode: '+852',
39734         
39735         /**
39736          * @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
39737          */
39738         preferedCountries: false,
39739         
39740         getAutoCreate : function()
39741         {
39742             var data = Roo.bootstrap.PhoneInputData();
39743             var align = this.labelAlign || this.parentLabelAlign();
39744             var id = Roo.id();
39745             
39746             this.allCountries = [];
39747             this.dialCodeMapping = [];
39748             
39749             for (var i = 0; i < data.length; i++) {
39750               var c = data[i];
39751               this.allCountries[i] = {
39752                 name: c[0],
39753                 iso2: c[1],
39754                 dialCode: c[2],
39755                 priority: c[3] || 0,
39756                 areaCodes: c[4] || null
39757               };
39758               this.dialCodeMapping[c[2]] = {
39759                   name: c[0],
39760                   iso2: c[1],
39761                   priority: c[3] || 0,
39762                   areaCodes: c[4] || null
39763               };
39764             }
39765             
39766             var cfg = {
39767                 cls: 'form-group',
39768                 cn: []
39769             };
39770             
39771             var input =  {
39772                 tag: 'input',
39773                 id : id,
39774                 cls : 'form-control tel-input',
39775                 autocomplete: 'new-password'
39776             };
39777             
39778             var hiddenInput = {
39779                 tag: 'input',
39780                 type: 'hidden',
39781                 cls: 'hidden-tel-input'
39782             };
39783             
39784             if (this.name) {
39785                 hiddenInput.name = this.name;
39786             }
39787             
39788             if (this.disabled) {
39789                 input.disabled = true;
39790             }
39791             
39792             var flag_container = {
39793                 tag: 'div',
39794                 cls: 'flag-box',
39795                 cn: [
39796                     {
39797                         tag: 'div',
39798                         cls: 'flag'
39799                     },
39800                     {
39801                         tag: 'div',
39802                         cls: 'caret'
39803                     }
39804                 ]
39805             };
39806             
39807             var box = {
39808                 tag: 'div',
39809                 cls: this.hasFeedback ? 'has-feedback' : '',
39810                 cn: [
39811                     hiddenInput,
39812                     input,
39813                     {
39814                         tag: 'input',
39815                         cls: 'dial-code-holder',
39816                         disabled: true
39817                     }
39818                 ]
39819             };
39820             
39821             var container = {
39822                 cls: 'roo-select2-container input-group',
39823                 cn: [
39824                     flag_container,
39825                     box
39826                 ]
39827             };
39828             
39829             if (this.fieldLabel.length) {
39830                 var indicator = {
39831                     tag: 'i',
39832                     tooltip: 'This field is required'
39833                 };
39834                 
39835                 var label = {
39836                     tag: 'label',
39837                     'for':  id,
39838                     cls: 'control-label',
39839                     cn: []
39840                 };
39841                 
39842                 var label_text = {
39843                     tag: 'span',
39844                     html: this.fieldLabel
39845                 };
39846                 
39847                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39848                 label.cn = [
39849                     indicator,
39850                     label_text
39851                 ];
39852                 
39853                 if(this.indicatorpos == 'right') {
39854                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39855                     label.cn = [
39856                         label_text,
39857                         indicator
39858                     ];
39859                 }
39860                 
39861                 if(align == 'left') {
39862                     container = {
39863                         tag: 'div',
39864                         cn: [
39865                             container
39866                         ]
39867                     };
39868                     
39869                     if(this.labelWidth > 12){
39870                         label.style = "width: " + this.labelWidth + 'px';
39871                     }
39872                     if(this.labelWidth < 13 && this.labelmd == 0){
39873                         this.labelmd = this.labelWidth;
39874                     }
39875                     if(this.labellg > 0){
39876                         label.cls += ' col-lg-' + this.labellg;
39877                         input.cls += ' col-lg-' + (12 - this.labellg);
39878                     }
39879                     if(this.labelmd > 0){
39880                         label.cls += ' col-md-' + this.labelmd;
39881                         container.cls += ' col-md-' + (12 - this.labelmd);
39882                     }
39883                     if(this.labelsm > 0){
39884                         label.cls += ' col-sm-' + this.labelsm;
39885                         container.cls += ' col-sm-' + (12 - this.labelsm);
39886                     }
39887                     if(this.labelxs > 0){
39888                         label.cls += ' col-xs-' + this.labelxs;
39889                         container.cls += ' col-xs-' + (12 - this.labelxs);
39890                     }
39891                 }
39892             }
39893             
39894             cfg.cn = [
39895                 label,
39896                 container
39897             ];
39898             
39899             var settings = this;
39900             
39901             ['xs','sm','md','lg'].map(function(size){
39902                 if (settings[size]) {
39903                     cfg.cls += ' col-' + size + '-' + settings[size];
39904                 }
39905             });
39906             
39907             this.store = new Roo.data.Store({
39908                 proxy : new Roo.data.MemoryProxy({}),
39909                 reader : new Roo.data.JsonReader({
39910                     fields : [
39911                         {
39912                             'name' : 'name',
39913                             'type' : 'string'
39914                         },
39915                         {
39916                             'name' : 'iso2',
39917                             'type' : 'string'
39918                         },
39919                         {
39920                             'name' : 'dialCode',
39921                             'type' : 'string'
39922                         },
39923                         {
39924                             'name' : 'priority',
39925                             'type' : 'string'
39926                         },
39927                         {
39928                             'name' : 'areaCodes',
39929                             'type' : 'string'
39930                         }
39931                     ]
39932                 })
39933             });
39934             
39935             if(!this.preferedCountries) {
39936                 this.preferedCountries = [
39937                     'hk',
39938                     'gb',
39939                     'us'
39940                 ];
39941             }
39942             
39943             var p = this.preferedCountries.reverse();
39944             
39945             if(p) {
39946                 for (var i = 0; i < p.length; i++) {
39947                     for (var j = 0; j < this.allCountries.length; j++) {
39948                         if(this.allCountries[j].iso2 == p[i]) {
39949                             var t = this.allCountries[j];
39950                             this.allCountries.splice(j,1);
39951                             this.allCountries.unshift(t);
39952                         }
39953                     } 
39954                 }
39955             }
39956             
39957             this.store.proxy.data = {
39958                 success: true,
39959                 data: this.allCountries
39960             };
39961             
39962             return cfg;
39963         },
39964         
39965         initEvents : function()
39966         {
39967             this.createList();
39968             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39969             
39970             this.indicator = this.indicatorEl();
39971             this.flag = this.flagEl();
39972             this.dialCodeHolder = this.dialCodeHolderEl();
39973             
39974             this.trigger = this.el.select('div.flag-box',true).first();
39975             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39976             
39977             var _this = this;
39978             
39979             (function(){
39980                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39981                 _this.list.setWidth(lw);
39982             }).defer(100);
39983             
39984             this.list.on('mouseover', this.onViewOver, this);
39985             this.list.on('mousemove', this.onViewMove, this);
39986             this.inputEl().on("keyup", this.onKeyUp, this);
39987             
39988             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39989
39990             this.view = new Roo.View(this.list, this.tpl, {
39991                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39992             });
39993             
39994             this.view.on('click', this.onViewClick, this);
39995             this.setValue(this.defaultDialCode);
39996         },
39997         
39998         onTriggerClick : function(e)
39999         {
40000             Roo.log('trigger click');
40001             if(this.disabled){
40002                 return;
40003             }
40004             
40005             if(this.isExpanded()){
40006                 this.collapse();
40007                 this.hasFocus = false;
40008             }else {
40009                 this.store.load({});
40010                 this.hasFocus = true;
40011                 this.expand();
40012             }
40013         },
40014         
40015         isExpanded : function()
40016         {
40017             return this.list.isVisible();
40018         },
40019         
40020         collapse : function()
40021         {
40022             if(!this.isExpanded()){
40023                 return;
40024             }
40025             this.list.hide();
40026             Roo.get(document).un('mousedown', this.collapseIf, this);
40027             Roo.get(document).un('mousewheel', this.collapseIf, this);
40028             this.fireEvent('collapse', this);
40029             this.validate();
40030         },
40031         
40032         expand : function()
40033         {
40034             Roo.log('expand');
40035
40036             if(this.isExpanded() || !this.hasFocus){
40037                 return;
40038             }
40039             
40040             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40041             this.list.setWidth(lw);
40042             
40043             this.list.show();
40044             this.restrictHeight();
40045             
40046             Roo.get(document).on('mousedown', this.collapseIf, this);
40047             Roo.get(document).on('mousewheel', this.collapseIf, this);
40048             
40049             this.fireEvent('expand', this);
40050         },
40051         
40052         restrictHeight : function()
40053         {
40054             this.list.alignTo(this.inputEl(), this.listAlign);
40055             this.list.alignTo(this.inputEl(), this.listAlign);
40056         },
40057         
40058         onViewOver : function(e, t)
40059         {
40060             if(this.inKeyMode){
40061                 return;
40062             }
40063             var item = this.view.findItemFromChild(t);
40064             
40065             if(item){
40066                 var index = this.view.indexOf(item);
40067                 this.select(index, false);
40068             }
40069         },
40070
40071         // private
40072         onViewClick : function(view, doFocus, el, e)
40073         {
40074             var index = this.view.getSelectedIndexes()[0];
40075             
40076             var r = this.store.getAt(index);
40077             
40078             if(r){
40079                 this.onSelect(r, index);
40080             }
40081             if(doFocus !== false && !this.blockFocus){
40082                 this.inputEl().focus();
40083             }
40084         },
40085         
40086         onViewMove : function(e, t)
40087         {
40088             this.inKeyMode = false;
40089         },
40090         
40091         select : function(index, scrollIntoView)
40092         {
40093             this.selectedIndex = index;
40094             this.view.select(index);
40095             if(scrollIntoView !== false){
40096                 var el = this.view.getNode(index);
40097                 if(el){
40098                     this.list.scrollChildIntoView(el, false);
40099                 }
40100             }
40101         },
40102         
40103         createList : function()
40104         {
40105             this.list = Roo.get(document.body).createChild({
40106                 tag: 'ul',
40107                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40108                 style: 'display:none'
40109             });
40110             
40111             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40112         },
40113         
40114         collapseIf : function(e)
40115         {
40116             var in_combo  = e.within(this.el);
40117             var in_list =  e.within(this.list);
40118             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40119             
40120             if (in_combo || in_list || is_list) {
40121                 return;
40122             }
40123             this.collapse();
40124         },
40125         
40126         onSelect : function(record, index)
40127         {
40128             if(this.fireEvent('beforeselect', this, record, index) !== false){
40129                 
40130                 this.setFlagClass(record.data.iso2);
40131                 this.setDialCode(record.data.dialCode);
40132                 this.hasFocus = false;
40133                 this.collapse();
40134                 this.fireEvent('select', this, record, index);
40135             }
40136         },
40137         
40138         flagEl : function()
40139         {
40140             var flag = this.el.select('div.flag',true).first();
40141             if(!flag){
40142                 return false;
40143             }
40144             return flag;
40145         },
40146         
40147         dialCodeHolderEl : function()
40148         {
40149             var d = this.el.select('input.dial-code-holder',true).first();
40150             if(!d){
40151                 return false;
40152             }
40153             return d;
40154         },
40155         
40156         setDialCode : function(v)
40157         {
40158             this.dialCodeHolder.dom.value = '+'+v;
40159         },
40160         
40161         setFlagClass : function(n)
40162         {
40163             this.flag.dom.className = 'flag '+n;
40164         },
40165         
40166         getValue : function()
40167         {
40168             var v = this.inputEl().getValue();
40169             if(this.dialCodeHolder) {
40170                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40171             }
40172             return v;
40173         },
40174         
40175         setValue : function(v)
40176         {
40177             var d = this.getDialCode(v);
40178             
40179             //invalid dial code
40180             if(v.length == 0 || !d || d.length == 0) {
40181                 if(this.rendered){
40182                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40183                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40184                 }
40185                 return;
40186             }
40187             
40188             //valid dial code
40189             this.setFlagClass(this.dialCodeMapping[d].iso2);
40190             this.setDialCode(d);
40191             this.inputEl().dom.value = v.replace('+'+d,'');
40192             this.hiddenEl().dom.value = this.getValue();
40193             
40194             this.validate();
40195         },
40196         
40197         getDialCode : function(v)
40198         {
40199             v = v ||  '';
40200             
40201             if (v.length == 0) {
40202                 return this.dialCodeHolder.dom.value;
40203             }
40204             
40205             var dialCode = "";
40206             if (v.charAt(0) != "+") {
40207                 return false;
40208             }
40209             var numericChars = "";
40210             for (var i = 1; i < v.length; i++) {
40211               var c = v.charAt(i);
40212               if (!isNaN(c)) {
40213                 numericChars += c;
40214                 if (this.dialCodeMapping[numericChars]) {
40215                   dialCode = v.substr(1, i);
40216                 }
40217                 if (numericChars.length == 4) {
40218                   break;
40219                 }
40220               }
40221             }
40222             return dialCode;
40223         },
40224         
40225         reset : function()
40226         {
40227             this.setValue(this.defaultDialCode);
40228             this.validate();
40229         },
40230         
40231         hiddenEl : function()
40232         {
40233             return this.el.select('input.hidden-tel-input',true).first();
40234         },
40235         
40236         onKeyUp : function(e){
40237             
40238             var k = e.getKey();
40239             var c = e.getCharCode();
40240             
40241             if(
40242                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40243                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40244             ){
40245                 e.stopEvent();
40246             }
40247             
40248             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40249             //     return;
40250             // }
40251             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40252                 e.stopEvent();
40253             }
40254             
40255             this.setValue(this.getValue());
40256         }
40257         
40258 });
40259 /**
40260  * @class Roo.bootstrap.MoneyField
40261  * @extends Roo.bootstrap.ComboBox
40262  * Bootstrap MoneyField class
40263  * 
40264  * @constructor
40265  * Create a new MoneyField.
40266  * @param {Object} config Configuration options
40267  */
40268
40269 Roo.bootstrap.MoneyField = function(config) {
40270     
40271     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40272     
40273 };
40274
40275 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40276     
40277     /**
40278      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40279      */
40280     allowDecimals : true,
40281     /**
40282      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40283      */
40284     decimalSeparator : ".",
40285     /**
40286      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40287      */
40288     decimalPrecision : 0,
40289     /**
40290      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40291      */
40292     allowNegative : true,
40293     /**
40294      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40295      */
40296     allowZero: true,
40297     /**
40298      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40299      */
40300     minValue : Number.NEGATIVE_INFINITY,
40301     /**
40302      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40303      */
40304     maxValue : Number.MAX_VALUE,
40305     /**
40306      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40307      */
40308     minText : "The minimum value for this field is {0}",
40309     /**
40310      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40311      */
40312     maxText : "The maximum value for this field is {0}",
40313     /**
40314      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40315      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40316      */
40317     nanText : "{0} is not a valid number",
40318     /**
40319      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40320      */
40321     castInt : true,
40322     /**
40323      * @cfg {String} defaults currency of the MoneyField
40324      * value should be in lkey
40325      */
40326     defaultCurrency : false,
40327     /**
40328      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40329      */
40330     thousandsDelimiter : false,
40331     
40332     
40333     inputlg : 9,
40334     inputmd : 9,
40335     inputsm : 9,
40336     inputxs : 6,
40337     
40338     store : false,
40339     
40340     getAutoCreate : function()
40341     {
40342         var align = this.labelAlign || this.parentLabelAlign();
40343         
40344         var id = Roo.id();
40345
40346         var cfg = {
40347             cls: 'form-group',
40348             cn: []
40349         };
40350
40351         var input =  {
40352             tag: 'input',
40353             id : id,
40354             cls : 'form-control roo-money-amount-input',
40355             autocomplete: 'new-password'
40356         };
40357         
40358         var hiddenInput = {
40359             tag: 'input',
40360             type: 'hidden',
40361             id: Roo.id(),
40362             cls: 'hidden-number-input'
40363         };
40364         
40365         if (this.name) {
40366             hiddenInput.name = this.name;
40367         }
40368
40369         if (this.disabled) {
40370             input.disabled = true;
40371         }
40372
40373         var clg = 12 - this.inputlg;
40374         var cmd = 12 - this.inputmd;
40375         var csm = 12 - this.inputsm;
40376         var cxs = 12 - this.inputxs;
40377         
40378         var container = {
40379             tag : 'div',
40380             cls : 'row roo-money-field',
40381             cn : [
40382                 {
40383                     tag : 'div',
40384                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40385                     cn : [
40386                         {
40387                             tag : 'div',
40388                             cls: 'roo-select2-container input-group',
40389                             cn: [
40390                                 {
40391                                     tag : 'input',
40392                                     cls : 'form-control roo-money-currency-input',
40393                                     autocomplete: 'new-password',
40394                                     readOnly : 1,
40395                                     name : this.currencyName
40396                                 },
40397                                 {
40398                                     tag :'span',
40399                                     cls : 'input-group-addon',
40400                                     cn : [
40401                                         {
40402                                             tag: 'span',
40403                                             cls: 'caret'
40404                                         }
40405                                     ]
40406                                 }
40407                             ]
40408                         }
40409                     ]
40410                 },
40411                 {
40412                     tag : 'div',
40413                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40414                     cn : [
40415                         {
40416                             tag: 'div',
40417                             cls: this.hasFeedback ? 'has-feedback' : '',
40418                             cn: [
40419                                 input
40420                             ]
40421                         }
40422                     ]
40423                 }
40424             ]
40425             
40426         };
40427         
40428         if (this.fieldLabel.length) {
40429             var indicator = {
40430                 tag: 'i',
40431                 tooltip: 'This field is required'
40432             };
40433
40434             var label = {
40435                 tag: 'label',
40436                 'for':  id,
40437                 cls: 'control-label',
40438                 cn: []
40439             };
40440
40441             var label_text = {
40442                 tag: 'span',
40443                 html: this.fieldLabel
40444             };
40445
40446             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40447             label.cn = [
40448                 indicator,
40449                 label_text
40450             ];
40451
40452             if(this.indicatorpos == 'right') {
40453                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40454                 label.cn = [
40455                     label_text,
40456                     indicator
40457                 ];
40458             }
40459
40460             if(align == 'left') {
40461                 container = {
40462                     tag: 'div',
40463                     cn: [
40464                         container
40465                     ]
40466                 };
40467
40468                 if(this.labelWidth > 12){
40469                     label.style = "width: " + this.labelWidth + 'px';
40470                 }
40471                 if(this.labelWidth < 13 && this.labelmd == 0){
40472                     this.labelmd = this.labelWidth;
40473                 }
40474                 if(this.labellg > 0){
40475                     label.cls += ' col-lg-' + this.labellg;
40476                     input.cls += ' col-lg-' + (12 - this.labellg);
40477                 }
40478                 if(this.labelmd > 0){
40479                     label.cls += ' col-md-' + this.labelmd;
40480                     container.cls += ' col-md-' + (12 - this.labelmd);
40481                 }
40482                 if(this.labelsm > 0){
40483                     label.cls += ' col-sm-' + this.labelsm;
40484                     container.cls += ' col-sm-' + (12 - this.labelsm);
40485                 }
40486                 if(this.labelxs > 0){
40487                     label.cls += ' col-xs-' + this.labelxs;
40488                     container.cls += ' col-xs-' + (12 - this.labelxs);
40489                 }
40490             }
40491         }
40492
40493         cfg.cn = [
40494             label,
40495             container,
40496             hiddenInput
40497         ];
40498         
40499         var settings = this;
40500
40501         ['xs','sm','md','lg'].map(function(size){
40502             if (settings[size]) {
40503                 cfg.cls += ' col-' + size + '-' + settings[size];
40504             }
40505         });
40506         
40507         return cfg;
40508     },
40509     
40510     initEvents : function()
40511     {
40512         this.indicator = this.indicatorEl();
40513         
40514         this.initCurrencyEvent();
40515         
40516         this.initNumberEvent();
40517     },
40518     
40519     initCurrencyEvent : function()
40520     {
40521         if (!this.store) {
40522             throw "can not find store for combo";
40523         }
40524         
40525         this.store = Roo.factory(this.store, Roo.data);
40526         this.store.parent = this;
40527         
40528         this.createList();
40529         
40530         this.triggerEl = this.el.select('.input-group-addon', true).first();
40531         
40532         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40533         
40534         var _this = this;
40535         
40536         (function(){
40537             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40538             _this.list.setWidth(lw);
40539         }).defer(100);
40540         
40541         this.list.on('mouseover', this.onViewOver, this);
40542         this.list.on('mousemove', this.onViewMove, this);
40543         this.list.on('scroll', this.onViewScroll, this);
40544         
40545         if(!this.tpl){
40546             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40547         }
40548         
40549         this.view = new Roo.View(this.list, this.tpl, {
40550             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40551         });
40552         
40553         this.view.on('click', this.onViewClick, this);
40554         
40555         this.store.on('beforeload', this.onBeforeLoad, this);
40556         this.store.on('load', this.onLoad, this);
40557         this.store.on('loadexception', this.onLoadException, this);
40558         
40559         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40560             "up" : function(e){
40561                 this.inKeyMode = true;
40562                 this.selectPrev();
40563             },
40564
40565             "down" : function(e){
40566                 if(!this.isExpanded()){
40567                     this.onTriggerClick();
40568                 }else{
40569                     this.inKeyMode = true;
40570                     this.selectNext();
40571                 }
40572             },
40573
40574             "enter" : function(e){
40575                 this.collapse();
40576                 
40577                 if(this.fireEvent("specialkey", this, e)){
40578                     this.onViewClick(false);
40579                 }
40580                 
40581                 return true;
40582             },
40583
40584             "esc" : function(e){
40585                 this.collapse();
40586             },
40587
40588             "tab" : function(e){
40589                 this.collapse();
40590                 
40591                 if(this.fireEvent("specialkey", this, e)){
40592                     this.onViewClick(false);
40593                 }
40594                 
40595                 return true;
40596             },
40597
40598             scope : this,
40599
40600             doRelay : function(foo, bar, hname){
40601                 if(hname == 'down' || this.scope.isExpanded()){
40602                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40603                 }
40604                 return true;
40605             },
40606
40607             forceKeyDown: true
40608         });
40609         
40610         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40611         
40612     },
40613     
40614     initNumberEvent : function(e)
40615     {
40616         this.inputEl().on("keydown" , this.fireKey,  this);
40617         this.inputEl().on("focus", this.onFocus,  this);
40618         this.inputEl().on("blur", this.onBlur,  this);
40619         
40620         this.inputEl().relayEvent('keyup', this);
40621         
40622         if(this.indicator){
40623             this.indicator.addClass('invisible');
40624         }
40625  
40626         this.originalValue = this.getValue();
40627         
40628         if(this.validationEvent == 'keyup'){
40629             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40630             this.inputEl().on('keyup', this.filterValidation, this);
40631         }
40632         else if(this.validationEvent !== false){
40633             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40634         }
40635         
40636         if(this.selectOnFocus){
40637             this.on("focus", this.preFocus, this);
40638             
40639         }
40640         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40641             this.inputEl().on("keypress", this.filterKeys, this);
40642         } else {
40643             this.inputEl().relayEvent('keypress', this);
40644         }
40645         
40646         var allowed = "0123456789";
40647         
40648         if(this.allowDecimals){
40649             allowed += this.decimalSeparator;
40650         }
40651         
40652         if(this.allowNegative){
40653             allowed += "-";
40654         }
40655         
40656         if(this.thousandsDelimiter) {
40657             allowed += ",";
40658         }
40659         
40660         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40661         
40662         var keyPress = function(e){
40663             
40664             var k = e.getKey();
40665             
40666             var c = e.getCharCode();
40667             
40668             if(
40669                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40670                     allowed.indexOf(String.fromCharCode(c)) === -1
40671             ){
40672                 e.stopEvent();
40673                 return;
40674             }
40675             
40676             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40677                 return;
40678             }
40679             
40680             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40681                 e.stopEvent();
40682             }
40683         };
40684         
40685         this.inputEl().on("keypress", keyPress, this);
40686         
40687     },
40688     
40689     onTriggerClick : function(e)
40690     {   
40691         if(this.disabled){
40692             return;
40693         }
40694         
40695         this.page = 0;
40696         this.loadNext = false;
40697         
40698         if(this.isExpanded()){
40699             this.collapse();
40700             return;
40701         }
40702         
40703         this.hasFocus = true;
40704         
40705         if(this.triggerAction == 'all') {
40706             this.doQuery(this.allQuery, true);
40707             return;
40708         }
40709         
40710         this.doQuery(this.getRawValue());
40711     },
40712     
40713     getCurrency : function()
40714     {   
40715         var v = this.currencyEl().getValue();
40716         
40717         return v;
40718     },
40719     
40720     restrictHeight : function()
40721     {
40722         this.list.alignTo(this.currencyEl(), this.listAlign);
40723         this.list.alignTo(this.currencyEl(), this.listAlign);
40724     },
40725     
40726     onViewClick : function(view, doFocus, el, e)
40727     {
40728         var index = this.view.getSelectedIndexes()[0];
40729         
40730         var r = this.store.getAt(index);
40731         
40732         if(r){
40733             this.onSelect(r, index);
40734         }
40735     },
40736     
40737     onSelect : function(record, index){
40738         
40739         if(this.fireEvent('beforeselect', this, record, index) !== false){
40740         
40741             this.setFromCurrencyData(index > -1 ? record.data : false);
40742             
40743             this.collapse();
40744             
40745             this.fireEvent('select', this, record, index);
40746         }
40747     },
40748     
40749     setFromCurrencyData : function(o)
40750     {
40751         var currency = '';
40752         
40753         this.lastCurrency = o;
40754         
40755         if (this.currencyField) {
40756             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40757         } else {
40758             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40759         }
40760         
40761         this.lastSelectionText = currency;
40762         
40763         //setting default currency
40764         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40765             this.setCurrency(this.defaultCurrency);
40766             return;
40767         }
40768         
40769         this.setCurrency(currency);
40770     },
40771     
40772     setFromData : function(o)
40773     {
40774         var c = {};
40775         
40776         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40777         
40778         this.setFromCurrencyData(c);
40779         
40780         var value = '';
40781         
40782         if (this.name) {
40783             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40784         } else {
40785             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40786         }
40787         
40788         this.setValue(value);
40789         
40790     },
40791     
40792     setCurrency : function(v)
40793     {   
40794         this.currencyValue = v;
40795         
40796         if(this.rendered){
40797             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40798             this.validate();
40799         }
40800     },
40801     
40802     setValue : function(v)
40803     {
40804         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40805         
40806         this.value = v;
40807         
40808         if(this.rendered){
40809             
40810             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40811             
40812             this.inputEl().dom.value = (v == '') ? '' :
40813                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40814             
40815             if(!this.allowZero && v === '0') {
40816                 this.hiddenEl().dom.value = '';
40817                 this.inputEl().dom.value = '';
40818             }
40819             
40820             this.validate();
40821         }
40822     },
40823     
40824     getRawValue : function()
40825     {
40826         var v = this.inputEl().getValue();
40827         
40828         return v;
40829     },
40830     
40831     getValue : function()
40832     {
40833         return this.fixPrecision(this.parseValue(this.getRawValue()));
40834     },
40835     
40836     parseValue : function(value)
40837     {
40838         if(this.thousandsDelimiter) {
40839             value += "";
40840             r = new RegExp(",", "g");
40841             value = value.replace(r, "");
40842         }
40843         
40844         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40845         return isNaN(value) ? '' : value;
40846         
40847     },
40848     
40849     fixPrecision : function(value)
40850     {
40851         if(this.thousandsDelimiter) {
40852             value += "";
40853             r = new RegExp(",", "g");
40854             value = value.replace(r, "");
40855         }
40856         
40857         var nan = isNaN(value);
40858         
40859         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40860             return nan ? '' : value;
40861         }
40862         return parseFloat(value).toFixed(this.decimalPrecision);
40863     },
40864     
40865     decimalPrecisionFcn : function(v)
40866     {
40867         return Math.floor(v);
40868     },
40869     
40870     validateValue : function(value)
40871     {
40872         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40873             return false;
40874         }
40875         
40876         var num = this.parseValue(value);
40877         
40878         if(isNaN(num)){
40879             this.markInvalid(String.format(this.nanText, value));
40880             return false;
40881         }
40882         
40883         if(num < this.minValue){
40884             this.markInvalid(String.format(this.minText, this.minValue));
40885             return false;
40886         }
40887         
40888         if(num > this.maxValue){
40889             this.markInvalid(String.format(this.maxText, this.maxValue));
40890             return false;
40891         }
40892         
40893         return true;
40894     },
40895     
40896     validate : function()
40897     {
40898         if(this.disabled || this.allowBlank){
40899             this.markValid();
40900             return true;
40901         }
40902         
40903         var currency = this.getCurrency();
40904         
40905         if(this.validateValue(this.getRawValue()) && currency.length){
40906             this.markValid();
40907             return true;
40908         }
40909         
40910         this.markInvalid();
40911         return false;
40912     },
40913     
40914     getName: function()
40915     {
40916         return this.name;
40917     },
40918     
40919     beforeBlur : function()
40920     {
40921         if(!this.castInt){
40922             return;
40923         }
40924         
40925         var v = this.parseValue(this.getRawValue());
40926         
40927         if(v || v == 0){
40928             this.setValue(v);
40929         }
40930     },
40931     
40932     onBlur : function()
40933     {
40934         this.beforeBlur();
40935         
40936         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40937             //this.el.removeClass(this.focusClass);
40938         }
40939         
40940         this.hasFocus = false;
40941         
40942         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40943             this.validate();
40944         }
40945         
40946         var v = this.getValue();
40947         
40948         if(String(v) !== String(this.startValue)){
40949             this.fireEvent('change', this, v, this.startValue);
40950         }
40951         
40952         this.fireEvent("blur", this);
40953     },
40954     
40955     inputEl : function()
40956     {
40957         return this.el.select('.roo-money-amount-input', true).first();
40958     },
40959     
40960     currencyEl : function()
40961     {
40962         return this.el.select('.roo-money-currency-input', true).first();
40963     },
40964     
40965     hiddenEl : function()
40966     {
40967         return this.el.select('input.hidden-number-input',true).first();
40968     }
40969     
40970 });